Back to atlas

Grounding-gated trust

Core Memory

A claim-and-bead memory whose epistemic grounding sets a ceiling on how trusted a record can ever become, so a speculative memory cannot reach canonical status by being used a lot.

Carries 4 of 7 rubric mechanisms. Most systems here carry none or one (66%), and a dash means the mechanism was not found at this commit — not that the system needed it.

  • Tombstone
  • Trust state
  • Bi-temporal
  • Scope enforced
  • Mutation audit
  • Human review
  • Negative evals

1. Executive Summary

Core Memory is an Apache-2.0 Python system of roughly 78,000 lines under core_memory/, with a documentation tree that reads like a specification and a test suite of over three hundred files. It is the largest thing this atlas has reviewed that is aimed squarely at the problem the atlas says matters most.

The mechanism worth the whole review is this. Every record carries an epistemic groundinghow do we know this? — and grounding caps how trusted the record can ever become:

grounding Meaning Effect on the C/B/A ladder
observed primary source, system of record, direct user statement enters at B; can reach A
extracted parsed from a document or structured field enters at B; can reach A
inferred agent reasoning over beads enters at C; A only via promotion or confirmation
speculative hypothesis or overlay, untested enters at C; capped at B until validated

The documentation states the consequence plainly: it makes the "why didn't this incorrect thing become permanent?" guarantee structural. A speculative bead cannot reach canonical status — "not via recall, not even via promotion." The only exit is for the grounding itself to upgrade, either because a hypothesis_status flips to validated or because a human confirms it, which lifts speculative to inferred.

Set that against the failure this atlas has documented repeatedly. In Holographic, user feedback moves a trust score and a popular error becomes durable. The decay and reinforcement pattern exists because systems collapse "how sure am I" into "how findable is this". Core Memory does not merely separate those axes — it makes one dominate the other in the safe direction. Use can raise a record's class, but never past the ceiling its grounding allows.

And it separates three axes where most systems have one:

grounding          how do we know it?        observed | extracted | inferred | speculative
confidence_class   how vetted is it?         C | B | A   (monotonic, never lowered)
status             is it still true?         open | superseded | retracted | ...

The docs draw the distinction explicitly against use-strength, which lives somewhere else entirely: confidence class is "the truth/governance status", myelination is "edge / use strength", and the table naming which answers "why didn't this incorrect thing become permanent?" versus "why did retrieval prefer this path?" is the cleanest statement of that separation anywhere in this atlas.

Reservations. The surface is enormous — thirteen subpackages, a soul module, a dreamer, myelination rewards, four storage backends — and large surfaces carry maintenance and comprehension costs that a smaller design does not. Correction is record-keyed rather than value-keyed, so supersession does not block a claim being re-derived from retained material. And the documentation is so thorough that it is occasionally ahead of what was verified in code here.

2. Mental Model

Two record types and three independent axes on the first:

Bead   typed record: decision, lesson, hypothesis, evidence, transcript,
       operational_event, structured_observation, document_reference, ...
       + scope, authority, grounding, confidence_class, status
       + approval_status / approved_by / approved_at / approval_note
       + source_turn_ids, prev/next_bead_id, type_log (append-only)
       + what_almost_happened, what_was_rejected, what_felt_risky, assumption

Claim  subject / slot / value — a discrete stated or inferred fact
       + observed_at, recorded_at        (when true vs when recorded)
       + effective_from, effective_to    (validity interval)
       + context_scope, confidence, reason_text

ClaimUpdate  a decision about an existing claim
       + decision, target_claim_id, replacement_claim_id
       + trigger_bead_id, grounding_hash, reason_text

Lifecycle of trust:

write → grounding assigned (GROUNDING_BY_TYPE, or set by a connector)
      → confidence_class starts at the floor grounding allows
      → recall / promotion / confirmation raise the class, monotonically
      → but never above the ceiling grounding permits
      → wrong? not demoted — superseded, which removes it from current truth
      → not memory-worthy? rejected, excluded unconditionally, retained for audit

3. Architecture

Subpackage sizes, in lines of Python:

  • runtime/ (20,090) — engine, turn handling, passes, flush, queue, ingest, the dreamer, semantic tasks, observability.
  • persistence/ (12,061) — store.py plus roughly forty store_*_ops.py modules, archive index, encryption, entity registry and merge flow, myelination manifest and rewards, promotion service, rolling record store.
  • integrations/ (12,024) — HTTP server, MCP, PydanticAI, CrewAI, Spring AI, an OpenClaw bridge.
  • retrieval/ (9,954), soul/ (3,491), schema/ (3,480), cli/ (3,271), graph/ (2,943), policy/ (2,574), association/ (1,994), claim/ (1,852), management/ (1,451), write_pipeline/ (666).
flowchart TD
  Turn["turn / connector<br/>ingest"] --> WP["write pipeline"]
  WP --> G{"grounding\nby<br/>type or connector"}
  G --> Bead["bead: class<br/>floored by<br/>grounding"]
  Bead --> Store["session JSONL<br/>(authority)"]
  Store --> Proj["index projection<br/>(rebuildable)"]
  Bead --> Claims["claim extraction:<br/>subject/slot/value"]
  Claims --> CU["claim updates:<br/>reaffirm / supersede /<br/>retract"]
  Proj --> Ret["typed retrieval: lexical ·<br/>semantic · entity ·<br/>causal"]
  Ret --> Answer["answer"]
  Ret -. "recall raises class, never past the ceiling" .-> Bead
  Dream["dreamer"] --> Cand["candidates"]
  Cand --> Human{"human decides"}
  Human --> Bead
  Bead --> Appr{"approval_status"}
  Appr -->|approved| A["class A · authority=user_confirmed"]
  Appr -->|rejected| Excl["excluded from<br/>truth, retained<br/>for audit"]

4. Essential Implementation Paths

Grounding as a ceiling, not a score

GROUNDING_BY_TYPE assigns a default from the bead's type: external and source-bearing types (structured_observation, operational_event, document_reference, transcript) and evidence default to observed; agent reasoning types (decision, lesson, state_assertion, data_insight) to inferred; hypothesis to speculative. A connector may set it explicitly — a claim parsed out of a document becomes extracted.

Two consequences follow, and both are unusual.

A primary-source observation is trusted from birth. It enters at B rather than C "because it is supported by its source before any use". Most systems here start everything equal and let reinforcement sort it out, which means a well-sourced fact and a guess begin indistinguishable.

A hypothesis cannot be promoted into canon. The cap is not a policy applied at promotion time that a caller might bypass; it is a property of the record that promotion respects. This is the structural version of what Verel achieves procedurally by restricting promotion, and what Atomic Agent approximates by shipping features off by default.

Monotonic class, with correction on a different axis

"The class never lowers. An incorrect bead doesn't get demoted — it gets superseded (status change + supersession chain), which removes it from current-truth retrieval entirely."

That sentence resolves a confusion running through most of this atlas. If one number carries both "how vetted" and "is this true", then correcting a fact looks like distrusting the process that produced it, and reinforcement looks like verification. Splitting them means a well-vetted claim that turned out wrong is superseded at class A — the record of how carefully it was established survives the discovery that it was false, which is exactly what you want when auditing how the mistake happened.

Bead.from_dict also raises a stored class to the floor implied by lifecycle fields (promoted, authority=user_confirmed, recall_count, promotion_candidate), so old records read with a correct class without a migration step.

The approval workflow, and its two deliberate choices

approval_status is pending | approved | rejected, with approved_by, approved_at and approval_note beside it. Four operations — request_approval, approve, reject, list_pending_approvals — are mirrored across the Python API, HTTP, MCP tools, and PydanticAI tools, each emitting an event and writing a full bead snapshot to the session archive so rebuild_index() preserves the record.

The reasoning attached to it is better than the mechanism:

  • Pending beads stay retrievable. "Hard-gating every auto-written bead until a human clicks approve would make memory useless until the queue is drained. The queue exists to surface what needs review; rejection is the only state that removes." This is the failure mode a naive review gate walks into, named and avoided.
  • Rejection is not provenance history. Superseded versions are surfaced via include_superseded because they were once true. A rejected bead was judged not memory-worthy, so it is excluded from retrieval unconditionally — but retained in the index with the rejecter and reason, for audit.

That distinction, between "was true and no longer is" and "should never have been recorded", is one this atlas has wanted repeatedly and found almost nowhere. Approving a speculative bead lifts its grounding to inferred, so class A stays consistent with the speculative ceiling rather than punching through it.

Every governance action requires a reason

The action validator in management/ rejects tombstone_bead, reject_memory and their siblings when no reason is supplied — reason_required is a validation error, not a warning. And tombstone_bead refuses more than one id:

elif len(bead_ids) > 1:
    # tombstone_bead is the single-bead semantic action; bulk removal must
    # go through remove_beads so the host opts into multi-bead intent.
    errors.append(_validation_error("targets.bead_id", "single_bead_only_use_remove_beads"))

Forcing bulk deletion through a different verb so the caller opts into the blast radius is a small, cheap, transferable idea, and this is the only instance of it in the atlas.

Bi-temporal claims

Claim carries observed_at and recorded_at — when it was true versus when the system learned it — alongside an effective_from / effective_to validity interval and a context_scope. ClaimUpdate records a decision about an existing claim with target_claim_id, an optional replacement_claim_id, a trigger_bead_id, and a grounding_hash binding the decision to the material that justified it.

This is the bi-temporal fact validity shape, arrived at independently of Graphiti, and with the update itself as a first-class retained record rather than an implicit edge mutation.

Contrast fields

Beads carry what_almost_happened, what_was_rejected, what_felt_risky, and assumption. Nothing else in this atlas stores the road not taken as a schema field.

Be precise about what this is: what_was_rejected is narrative about a decision, not a rejected-value tombstone. It does not block re-assertion and is not keyed on a value. But it captures something every other system discards — that an agent considered three approaches and took one — which is exactly the context a later reader needs to avoid relitigating a settled question.

5. Memory Data Model

The bead is unusually wide: type, title, summary, detail, scope, authority, confidence, tags, links, status, recall_count, last_recalled, source_turn_ids, turn_index, prev_bead_id/next_bead_id, an append-only type_log recording type progression, retrieval_eligible with separate retrieval_title and retrieval_facts, entity and topic lists, and keyed buckets for incidents, decisions, goals and actions.

retrieval_eligible deserves note: whether a record participates in retrieval is a stored property distinct from whether it exists, so capture and recall are not the same decision.

The gap is the one this atlas always finds. Supersession, retraction and tombstoning are keyed on bead id. Nothing found here records that a value was judged wrong in a way that would block the same value being extracted again from retained turns. Core Memory gets closer than most — rejected excludes unconditionally and retains the rejecter and reason — but that is a statement about a record, and re-extraction produces a new one.

6. Retrieval Mechanics

A typed retrieval pipeline resolving against "canonical archive/graph/projection surfaces", with lexical, semantic, entity-aware and causal paths, an intent-weighted reranker, query expansion, and deep recall. Graph backends are pluggable across Qdrant, Kuzu and Neo4j with a parity test suite; semantic backends have explicit modes and a semantic_doctor.

Association edges carry myelination — use-strength weights and bonuses that influence traversal. Keeping that on the edges while confidence class lives on the bead is the structural expression of the separation described above: the graph learns which paths are worn, and no amount of wearing changes how vetted a record is.

include_superseded makes historical retrieval an explicit request rather than a leak.

7. Write Mechanics

Turns are captured into beads through a write pipeline with an enrichment queue, gate severity, and rolling dedup. Claims are extracted with their own registry, resolver and update policy. Connectors ingest external material and may set grounding per source. Side effects run through a queue with flush checkpoints and recovery tests.

The live authority is .beads/session-<id>.jsonl; the index projection is a rebuildable derivative and, per the truth-hierarchy document, may serve as a fallback only when explicitly enabled. That is the evidence before belief discipline stated as a document rather than left implicit — and docs/truth_hierarchy.md goes further, naming MEMORY.md as "an OpenClaw parallel surface and not a canonical Core Memory runtime/storage truth source", which is the kind of boundary that otherwise gets discovered during an incident.

8. Agent Integration

Python API, an HTTP server, MCP (including typed reads and writes), PydanticAI tools, CrewAI, a Spring AI adapter, and an OpenClaw bridge with its own doctor and CI smoke scripts. An adapter parity matrix and contract tests exist to keep those surfaces from drifting apart.

The dreamer proposes candidates that a human decides on — DreamerCandidateDecideRequest on the HTTP surface — so background consolidation is a proposal mechanism rather than an autonomous rewriter. That places it with Memora's dry-run default and against CowAgent's nightly overwrite.

9. Reliability, Safety, and Trust

Strengths:

  • Grounding caps the trust ladder, making "an incorrect thing cannot become permanent" structural rather than procedural.
  • Three separate axes: how known, how vetted, whether still true.
  • Monotonic class, so correction and vetting do not contaminate each other.
  • Rejected distinguished from superseded, with retention for audit either way.
  • A reason is mandatory on every governance action.
  • Bulk deletion needs a different verb.
  • Bi-temporal claims with retained update decisions and a grounding hash.
  • A documented truth hierarchy naming the authoritative surface per concern.
  • Human-decided background proposals rather than autonomous rewriting.
  • Committed benchmark harnesses for LoCoMo, LongMemEval, and causal continuity, plus KPI targets and a dreamer eval.
  • Secret redaction and encryption modules with dedicated tests.

Gaps:

  • No value-level tombstone. Correction is record-keyed, so re-extraction is unblocked.
  • A very large surface. Thirteen subpackages and forty store_*_ops modules is a lot of system to keep coherent, and the documentation tree has its own archive of superseded plans.
  • Grounding is assigned, not verified. GROUNDING_BY_TYPE trusts the type, and a connector asserting observed is taken at its word — the ceiling is only as good as the honesty of whatever set it.
  • retrieval_eligible defaults to False, so what participates in recall depends on a pipeline step, and a failure there is silent under-retrieval.
  • Recall raises the class, which is reinforcement — bounded by the ceiling, but still a use signal feeding a trust field.

10. Tests, Evals, and Benchmarks

Over three hundred test files, and the names track the risky logic closely: test_supersession_guards.py, test_epistemic_conflict.py, test_conflict_review.py, test_approval_workflow.py, test_claim_update_policy.py, test_recall_as_of.py, test_secret_redaction.py, test_authority_enforcement.py, test_retrieval_path_purity.py, test_architecture_guards.py.

benchmarks/ contains locomo, locomo_like, longmemeval, causal and causal_continuity trees; eval/ adds retrieval_eval.py, longitudinal_benchmark_v2.py, paraphrase_eval.py, dreamer_behavior_eval.py and a kpi_set.json.

Nothing was run for this review, and no scored result artifacts were located, so this sits in the same category the atlas has flagged elsewhere: a reproducible harness is not a reproduced result. The distinctive claim — that grounding prevents speculative memory from becoming canonical — is testable directly and cheaply, and no evidence of it having been measured was found.

11. For Your Own Build

Steal

  • Let epistemic grounding cap the trust ladder. Reinforcement raising a record's standing is fine; reinforcement raising it past what its source justifies is how popular errors become canon. A ceiling per grounding kind is a few lines and closes that path structurally.
  • Trust source-supported records from birth. Entering at B rather than C because a primary source backs it, rather than making everything earn its way up equally, is both more accurate and cheaper.
  • Make the trust class monotonic and correct on a different axis. Superseding a class-A claim preserves the record of how carefully it was established.
  • Distinguish rejected from superseded. "Was true, no longer is" and "should never have been recorded" need different retrieval semantics — the first surfaced on request, the second excluded unconditionally.
  • Keep the review queue non-blocking. Pending stays retrievable; only rejection removes. A review gate that quarantines everything makes memory useless until someone drains the queue.
  • Require a reason on every governance action, enforced as a validation error.
  • Force bulk removal through a separate verb, so a caller opts into the blast radius rather than passing a longer list.
  • Write the truth hierarchy down. Naming which surface is authoritative per concern, including which parallel surfaces are explicitly not canonical, prevents a class of incident that otherwise gets discovered during one.
  • Store the road not taken. what_was_rejected, what_almost_happened and assumption retain context every other system in this atlas discards.

Avoid

  • Supersession without a value-level tombstone, in a system with connector ingest and re-extraction.
  • Grounding asserted rather than proven — the ceiling inherits the honesty of whatever set the field.
  • Surface area large enough that the documentation needs its own archive of superseded plans.
  • Recall feeding the trust class, even bounded.

Fit

Borrow:

  • The grounding-caps-the-ladder mechanism, which is the single most transferable idea in this report and is roughly a lookup table plus a min().
  • The three-axis split, and the sentence explaining why demotion is the wrong correction.
  • Rejected-versus-superseded semantics and the non-blocking review queue.
  • Mandatory reasons and the single-versus-bulk verb split.

Do not copy:

  • The whole surface, unless you have a comparable maintenance budget.
  • Record-keyed correction as the complete story if extraction can re-derive.

12. Open Questions

  • Does anything verify a connector's grounding claim, or is the ceiling advisory once a source asserts observed?
  • What prevents a rejected bead's content being re-extracted from the retained turns that produced it?
  • Have the LoCoMo and LongMemEval harnesses been run, and where are the results?
  • How does retrieval_eligible get set, and what detects the silent under-retrieval that a failure there produces?
  • Do the four graph backends produce equivalent rankings, or only equivalent APIs? The parity tests suggest the question was asked.
  • Does the OpenClaw bridge carry grounding and confidence class across the boundary, or flatten them?

Appendix: File Index

  • Schema: core_memory/schema/models.py (Bead, Claim, ClaimUpdate, ClaimKind, grounding and confidence fields).
  • Trust documentation: docs/confidence_class.md, docs/truth_hierarchy.md, docs/truth_hierarchy_policy.md, docs/approval_workflow.md, docs/claim_layer.md, docs/edge_lifecycle.md.
  • Governance validation: core_memory/management/__init__.py (tombstone_bead, reject_memory, reason_required).
  • Store: core_memory/persistence/store.py and the store_*_ops.py family, including store_approval_ops.py, store_claim_ops.py, store_promotion_ops.py.
  • Runtime and background: core_memory/runtime/dreamer/, runtime/passes/, runtime/queue/, runtime/flush/.
  • Retrieval: core_memory/retrieval/, core_memory/association/, core_memory/graph/.
  • Benchmarks: benchmarks/locomo/, benchmarks/longmemeval/, benchmarks/causal_continuity/, eval/kpi_set.json.