2  The mental model

Everything in claimgraph follows from a handful of commitments. This chapter goes over the mental model that’s useful to keep in mind in order to understand how claimgraph works.

2.1 A fact is a claim with metadata

The unit of storage is not a plain triple, think of it more like a reified edge. Claims are first-class records in the shape of a subject, predicate, object triple, but each one also carries its own bundle of metadata that enable the various key features of claimgraph. It looks like this:

{:id          "f-1a2b..."
 :subject     {:name "AuthService" :type :service}
 :predicate   :core/prefers
 :object-kind :literal            ; or :entity, and then :object-ref
 :object-lit  "argon2"
 :t-valid     #inst "2026-03-01"  ; when this became true of the world
 :t-invalid   nil                 ; nil means: still true
 :recorded-at #inst "2026-03-04"  ; when the store learned it
 :epistemic   :preference         ; observation | commitment | preference
 :confidence  0.8
 :source-type :user-assertion     ; code | user-assertion | decision-record |
                                  ; session-log | failure-report | agent-note |
                                  ; inferred
 :episode     "ep-…"              ; provenance anchor
 :scope       "project"
 :conflicts   []}                 ; links to rival facts, when flagged

These metadata are what lines of markdown can’t carry. Each field drives a certain behaviour. The timestamps power the time travel features of the system, the epistemic class drives conflict policy, the source type drives trust and confidence ceilings, the episode drives provenance and audit.

2.2 Two timestamps, and nothing is ever deleted

Valid time (t-valid to t-invalid) is when a fact was true of the world. Transaction time (recorded-at) is when the store learned it. Temporal databases have existed since the 1990s (e.g. Snodgrass). Modern databases like Datomic, XTDB, and Graphiti carried the pattern into modern stores, and claimgraph borrows this idea.

The most consequential effect of modelling time this way is that the past is immutable. Contradiction closes an interval, it does not erase a record. When has-version 2.0.0 supersedes has-version 1.0.0, the old fact’s t-invalid is set to the new fact’s t-valid and both remain queryable forever. Three query verbs fall out for free:

  • current truth: facts whose interval is open now,
  • as-of: facts whose interval contains an arbitrary timestamp,
  • history: every version of (subject, predicate), in order, with the reasons intervals closed.

This enables queries like “What did we believe in March, and why did it change”.

2.3 Three kinds of claims, three revision behaviors

Every fact carries an epistemic class, and the class decides what happens when a new fact contradicts an existing one:

Class What it is On contradiction
observation something derived or verifiable (“imports X”) supersede (close the old interval, keep it in history)
preference a stylistic or tooling stance (“prefers small PRs”) supersede
commitment a human decision (“we decided against GraphQL”) flag: both facts stay valid, the conflict surfaces for a human to review

Observations update themselves when the world changes. A commitment is a different kind of fact. It’s a decision someone made, often for reasons the code cannot show, and these should never be silently overwritten. Flagged conflicts stay open until a human (or an offline judge, for easy classes of contradictions) rules. This is one implementation of belief-revision theory’s insistence that entrenched beliefs need more than a newer timestamp to displace (see AGM 1985).

2.4 Fixed vocabulary

The predicate vocabulary encodes 23 curated core/* predicates by default, each carrying its object kind, cardinality, default epistemic class, and an anchor into established vocabularies (PROV-O, SPDX, DOAP, Dublin Core). Unknown predicates are rejected with a did-you-mean suggestion; genuinely new relations go to an x/* staging namespace and earn promotion by use. This keeps the vocabulary from degrading into mess of similar but unharmonized predicates, but also from going stale as legitimately new predicates enter the system.

2.5 Sources have rank, and confidence has ceilings

Where a fact came from bounds how much weight it can ever carry:

Source Trust rank (higher is more) Confidence ceiling (higher is more)
decision-record, user-assertion, code 3 1.0 / 0.9 / 0.95
session-log, failure-report 2 0.7
agent-note, inferred 1 0.65 / 0.6

Re-asserting an existing fact reinforces it. Facts decay in confidence over time, but if they are re-asserted their disuse clock resets and base confidence rises toward the source’s ceiling, but never above it, and never by repetition alone. This is how a fact the code ingester re-derives five hundred times stays distinguishable and always carries less weight than a human decision.

Rank powers two write-path defenses that exist because memory poisoning is a demonstrated attack (MINJA reached 98% injection success through ordinary queries), not a hypothetical:

  • Outranked writes cannot supersede. A session note cannot silently replace what a decision record established; it flags instead.
  • Revenant detection. When a low-trust source re-asserts a value that was previously invalidated and a live rival exists, the write is flagged against the rival rather than admitted as current truth. The resurrected claim gets a hearing, not a throne.

2.6 Episodes below, evidence below that

Every ingestion happens under an episode: a provenance record naming the source type and a stable reference (a git SHA, a session id, a note file at a content hash). Facts point at episodes; “which session said this, in what state” is always answerable.

Below episodes sits the raw-evidence tier. Extraction decides what to keep before any future query exists (the write-before-query barrier, TierMem), so the extractors keep their raw input as immutable, content-addressed artifacts in <db>.evidence/. Retrieval can escalate: graph facts first, episode summaries when the graph is silent, raw evidence lines when even the summaries are. Nothing an extractor drops is unrecoverable.

2.7 Forgetting is a view, not a job

Facts fade by disuse, and the fading is computed at read time. Each fact stores a base confidence and a last-reinforced timestamp; reads report an effective confidence, the base halved per 90 days since reinforcement. No batch job rewrites anything, --as-of queries see period-appropriate decay, and commitments and decision records never fade at all.

The design leans on an old result: memory retention tracks the statistics of use, and the odds an item is needed again decay smoothly with disuse (Anderson and Schooler, 1991). The important distinction is between disuse and falsity. A fact the code contradicts gets invalidated, mechanically, at ingest time. A fact nobody has restated in six months just fades in rank. The harness compacting its notes under space pressure stops reinforcing a fact; it does not make the fact false, and the machinery treats those differently.

Usefulness also counts. Read verbs log which facts they surfaced, and claim outcome accepted resets the disuse clocks of everything retrieved since the last mark. Retrieval in work that was accepted is evidence of aliveness. It never raises confidence; only sources do that.

2.8 The determinism boundary

One rule owns the write path: no LLM ever decides what the store believes.

The LLM appears in exactly three places, all upstream or offline:

  1. Extraction proposes candidate facts from transcripts and notes. Every candidate then passes through the same deterministic gauntlet as a manual assert: validation, admission scoring, conflict policy, trust checks.
  2. The judge classifies already-flagged conflict pairs, offline, with a confidence gate, and is never permitted to auto-resolve a genuine contradiction.
  3. Consolidation summarizes episodes, with a mechanical fallback when no model is available.

Everything else is a pure function. The 2026 freshness result (+10.8 points for deterministic versioning over LLM-mediated updates, Don’t Ask the LLM to Track Freshness) arrived after this rule was set, and said the rule was right.

2.9 The store is a view; the log is the record

Every mutation appends one effect line to the current writer’s own append-only log (<db>.oplog/<writer>.jsonl), stamped with a hybrid logical clock (Kulkarni et al., 2014). The live store is a materialized view over the logs, the same inversion event-sourced systems and replicated logs use (Kreps, “The Log”).

Because each machine appends only to its own file, any file syncer (git, rsync, Syncthing) can move logs between machines without a transport conflict, the property the local-first literature builds on (Kleppmann et al., 2019). claim reconcile replays unseen foreign effects in canonical clock order, matches entity identity by name, collapses claims both writers made independently, and queues the contradictions neither writer could see.

This is deliberately not a CRDT (Shapiro et al., 2011). A CRDT’s contract is convergence by construction: disagreement is merged away. In a memory system, two machines disagreeing about a fact is signal for a human, and claimgraph already has a first-class representation for that: an open conflict. Convergence here means both machines end up seeing the same disagreement.

2.10 The ambient loop

The pieces compose into a loop that runs without anyone learning a verb:

harness session → auto-memory notes → ingest-notes (delta-detected,
     ↑                                  inference-grade, full conflict
     │                                  machinery)
     │                                        ↓
inject at session start  ←  compile-context  ←  graph (+ consolidate, judge)

A SessionEnd hook runs the loop as two detached passes, so quitting a session costs a process spawn and never waits on a model. Capture is deterministic: a mechanical code-freshness pass (every detected language analyzer, delta-gated on the code episode’s <git-sha>+<dirty-digest> ref, so it costs nothing when nothing changed and reconciles teammates’ pulled changes when it did), then a recompile of the injected view. Capture then spawns a detached curator — notes ingest, consolidation, a final recompile — which owns every model call, under one budget whose every call lands a durable outcome. The compiled view (standing decisions, open conflicts, recent supersessions, top current facts) is written into a marker-delimited managed section of the file the harness already injects. The managed section is stripped before ingestion hashes its input, so the graph never re-consumes its own view: compile, ingest, compile is a fixed point.

2.11 The invariants

Stated once, holding everywhere; the executable chapters exercise most of them.

  1. No LLM on the write path. Ever.
  2. Invalidate, never delete. History is part of the data.
  3. Commitments are never auto-resolved, by the write path, the judge, or reconciliation.
  4. Reads never mutate. Decay is computed, not applied; the outcome log defers its writes to an explicit outcome verb.
  5. Reinforcement never exceeds the source ceiling and never comes from repetition alone.
  6. Entity ids are internal. Identity crosses machines and dumps by name and alias, and display names are local property.
  7. Deterministic where claimed: compile-context, the benchmark mechanics, and reconciliation ordering produce identical output from identical input, no exceptions.