Skip to content

Bi-Temporal Claim Store

mempill’s persistence layer is an append-only, bi-temporal claim store. Every claim is written once and never mutated. Supersession is recorded as a new bounded assertion that links back to the original — the history is never destroyed.

This design is not a coincidence of implementation. It is the only data model that can satisfy all of I1 through I4 simultaneously: you cannot have both non-destructive history (I1) and read-time-canonical belief (I3) if you allow in-place mutation.

Every claim carries two independent temporal dimensions:

Axis Stamped by Reliability Question answered
Transaction-time (tx_time) Engine Reliable, monotone, non-negotiable When did the engine record this claim?
Valid-time (valid_time) Caller Fallible, confidence-tagged, optional When was this claim true in the real world?

Transaction-time is assigned by the engine at write time and cannot be overridden by the caller. Valid-time is supplied by the caller as an optional bounded window [valid_from, valid_until) along with a valid_time_confidence score (0–1). When valid-time is omitted, the engine defaults the confidence to 0.0 and falls back to transaction-time ordering for belief ranking.

The two axes are kept separate because they are produced by different processes with different reliability. Conflating them — treating “when I wrote this” as “when this was true” — is invariant I2’s error case.

An immutable triple (subject, predicate, value) with additional fields:

Field Type Notes
agent_id string The agent whose memory this claim belongs to
subject string The entity the claim is about (e.g., "user", "project:123")
predicate string The property being asserted (e.g., "city", "status")
value JSON The claimed value
provenance ProvenanceLabel Which of the three channels wrote this — immutable
confidence {value_confidence, valid_time_confidence} Two independent quality scores
cardinality Functional | SetValued | Unknown Single-valued or multi-valued belief
criticality Low | Medium | High | Critical How important is this claim for ranking
valid_time optional {start, end} When the claim was true in the real world
derived_from list of ClaimRef Provenance chain links for derivation-depth tracking

Claims are INSERT-only. There is no UPDATE or DELETE method on the claim store. The Rust type system enforces this.

A separate immutable record that closes the valid-time window of an existing claim. Written by the Supersession component (C4) when a claim is superseded. The original claim remains in the store; only its temporal window is bounded by this assertion.

There are two assertion types:

  • Bound — closes an open-ended claim at a specified transaction time. Written by oracle-driven supersession or explicit invalidation.
  • Reopen — reopens a bounded claim. Written by the Reinstated path when a previously closed claim is revalidated.

An immutable audit record created for every disposition outcome. The AuditLedger (C8) maintains this table. Every ingest_claim call produces at least one ledger entry. Ledger entries are queryable via query_audit by agent_id, claim_ref, and transaction-time range.

Invariant I3 is the most important architectural consequence of this design: there is no “current belief” row. Belief is recomputed on every read by the TruthEngine (C2) performing a canonical valid-time fold over the full claim and assertion history.

The fold proceeds as follows:

  1. Load all claims for (agent_id, subject, predicate).
  2. Apply liveness filter: use validity assertions to determine which claims are live as of the query instant (controlled by as_of_tx_time).
  3. If all live functional claims have non-overlapping, trusted valid-time windows, select the one whose window contains the query instant (succession resolution — see Valid-Time Succession).
  4. If multiple live functional claims remain and their windows overlap (or lack trusted valid-time), compute has_conflict = true.
  5. Rank by: valid-time-covers-now → tx-recency (provenance-independent) → criticality.
  6. Return a BeliefProjection with status Resolved, TimingUncertain, Contested, or NoBelief.

These are the two non-conflict statuses a query can return:

  • Resolved — the fold found exactly one live claim whose valid_time window contains the query instant with valid_time_confidence >= 0.7. The engine has high confidence in both the value and when it applied.
  • TimingUncertain — the fold found a single live claim, but valid_time_confidence is below the threshold (or no valid-time was supplied). The value is the best available answer, but the engine is not confident about when exactly it applies.

A claim with TimingUncertain is not wrong — it is the honest signal for “we know what was asserted, but we are not sure when it was true.” This is why the quickstart shows TimingUncertain for a claim submitted without a valid-time window: no valid-time was given, so the confidence is 0.0.

  • I1 — Non-destruction: writes are INSERT-only. Supersession never deletes. History is retained forever.
  • I2 — Bi-temporal by trust: transaction-time is engine-stamped; valid-time is caller-supplied.
  • I3 — Belief derived, never stored: recomputed via canonical fold at read time.
  • I4 — Provenance immutable: set at injection time; no operation can rewrite it.
  • I8 — Read-time canonical: the fold is authoritative; materialized links are a subordinate cache.
  • I9 — Atomic commit unit: {claim + bounding assertion + ledger entry} commits as one indivisible unit scoped to one agent_id.

The primary read operation is query_memory:

result = engine.query_memory({
"agent_id": "my-agent",
"subject": "user",
"predicate": "city",
# Optional: as_of_tx_time restricts which ValidityAssertions are visible.
# It does NOT filter claims by their valid_time window.
"as_of_tx_time": "2025-01-01T00:00:00Z",
})

The as_of_tx_time parameter filters which validity assertions are visible at the query instant. It implements the transaction-time axis of the bi-temporal read: “what did the engine know, as of this transaction instant?” The valid-time axis is consulted separately for succession resolution and conflict detection.