# Reconciliation and supersession

How SurrealDB Agent Memory deduplicates entities, detects conflicts, and tracks corrections with full history.

Extracted data is not written directly to memory. A **reconciliation** pass compares new extractions against what is already stored: matching entities, detecting conflicts, and deciding whether a new value replaces an old one or coexists alongside it.

## Reconciliation in brief

### Entity matching

SurrealDB Agent Memory attaches a new extraction to an existing entity by **exact composite key**. An entity's record id is **`[normalised_type, normalised_name]`**, and matching is a direct lookup on that id: either the record exists and the new attributes and relations land on it, or a new entity is created. There is no fuzzy, phonetic, or embedding-based matching on the write path - a name that normalises differently becomes a separate entity.

Keeping references together therefore depends on **extraction emitting a consistent name**. To support that, the extraction prompt is given the entities the Context already holds and instructed to reuse their exact names. Seeding the entities you care about before a bulk ingest measurably improves the odds that later mentions land on them.

**Type is half the identity** - the same name under two types is two entities that nothing merges. Supplied types are normalised into a closed vocabulary (`person`, `organisation`, `project`, `location`, `topic`, `product`, `policy`, `concept`, `event`, `agent`, `service`, `other`). Near-synonyms fold into it - `company`, `org`, and `organization` all become `organisation` - and a type outside the set becomes **`other`** rather than minting a new type. When you write entities directly, pick the type once and keep it stable, or a later write lands on a different record.

**Nicknames and alternative names** - because matching is exact, a referent named two ways produces two entities. `Matt` and `Matthew Cauldwell`, `IBM` and `International Business Machines`, a person referred to by first name in one document and in full in another: each pair is two records, each with its own attributes and its own supersession chains.

There is no alias field and no automatic merge. Three things help:

- **Seed canonical names first.** Create the entity under the name you want before ingesting, so it appears in the extraction prompt's known-entity list.
- **Normalise at the edge.** Map the variants you know about to one canonical name in your own pipeline, before the write.
- **Audit with [`POST /fsck`](/docs/agent-memory/reference/rest-api.md).** The `duplicates` check reports entity pairs above a cosine-similarity floor (default **`0.95`**) in the same scope. It reports candidates for you to act on; it does not merge them.

> [!NOTE]
> A **`same_as`** relation is an ordinary edge label, not an instruction to the reconciler. It records that two entities denote the same referent - useful when that is a fact the reader or agent learns at a particular moment, and gate-able with **`asOf`** - but the two entities stay separate, keep their own attributes, and are returned separately. Merging them is a decision for the layer above.

**Cross-language mentions** - the same concept in different languages (for example `skill_math` from an English conversation and `skill_matte` from a Swedish conversation) are usually stored as **separate relation labels** unless extraction or reconciliation maps them to the same entity and key. That is expected: labels carry language-specific semantics. Background **elaboration** may later link related entities. Labels cannot be constrained up front, but extraction is shown the labels already in use and told to reuse them where the meaning matches - see [Extraction vocabulary](/docs/agent-memory/tuning/ontology-grounding.md). SurrealDB Agent Memory does not automatically mint a meta-edge between every cross-language synonym.

### Attribute conflicts

When the same entity and key already have a value in the **same scope** and the new value differs, SurrealDB Agent Memory treats it as a conflict. What happens depends on where the existing value came from:

- **Experiential-only conflict** - the new value **supersedes** the old. The previous value is closed with a **`valid_until`** timestamp and remains in history.
- **Authoritative conflict** - curated knowledge (documents, operator upserts) is **not** modified. The user’s assertion is stored in experiential memory with full provenance, and the clash is surfaced (for example via **`uncertainty`** records). See [Authority when pillars meet](/docs/agent-memory/reasoning/authority-hierarchy.md).

SurrealDB Agent Memory does **not** use last-write-wins. Concurrent updates reconcile in **one ACID transaction** per write; cross-provenance clashes become **`uncertainty`**, same-stream updates **supersede** when confidence allows, and writes below the **confidence floor** cannot silently overwrite stronger beliefs.

### Confidence floor

Each Context configures **`config.reconciliation.confidence_floor`** (default **`0.7`**). On a same-provenance conflict, SurrealDB Agent Memory supersedes the prior value only when the new extraction’s confidence is at or above the floor **and** at or above the existing row’s confidence. Otherwise both values stay live and an **`uncertainty`** is recorded - a low-confidence extraction cannot erase an established high-confidence fact.

### Scope-aware coexistence

Different scopes may legitimately hold different values for the same key - an org default and a user override, or the same person in two organisations. Conflict detection runs **within one scope**. Cross-scope differences are independent records, not supersessions.

### Invalidation

When a user explicitly negates a fact (“I no longer work there”, “forget my old address”), reconciliation **closes** matching active attributes with **`valid_until`** rather than deleting them. History and audit trails stay intact.

## The supersession chain

When a fact changes over time, SurrealDB Agent Memory keeps a **chain** of values - each link records what it replaced and what replaced it. Only the **current** head of the chain is returned by default queries; earlier values remain available for history and point-in-time reads.

Example: Alice’s role moves from COO → CEO → CTO. Each step opens a new value with **`valid_from`** set to the correction time and closes the previous one with **`valid_until`**. Nothing is erased.

## Querying current and historical state

Use the public API rather than ad hoc database queries:

- **Current beliefs** - **`POST /query`** or **`GET /profile`** for a scope; entity GETs return active attributes.
- **History for one key** - **`GET /api/v1/{context_id}/entities/{type}/{name}/history/{key}`** returns the supersession chain.
- **Point in time** - entity reads accept **`asOf`**, **`atInstant`**, **`validFrom`**, and **`validUntil`** query parameters. See [Tri-temporal model](/docs/agent-memory/architecture/tri-temporal-model.md) and the [REST API](/docs/agent-memory/reference/rest-api.md).

## Correction versus coexistence

**Correction** - same scope, new value replaces old reality (role change, corrected name). Supersession applies.

**Coexistence** - same key, different scopes, both valid in their contexts. Neither record supersedes the other.

### Unresolved relation objects

When reconciliation cannot resolve a relation’s object entity, it materialises a placeholder typed **`unknown`** so the stated fact is not dropped from recall. These placeholders may not auto-merge with a later properly typed extraction until a maintenance sweep reconciles them.

## History by default - and forget when you need removal

For **corrections and supersession**, SurrealDB Agent Memory keeps a full chain of past values. Nothing is erased: earlier beliefs stay in the store so you can audit, recover from a bad correction, or answer “what did we believe in January?”

That default does **not** mean users can never remove data. When someone asks to forget something, SurrealDB Agent Memory supports two distinct levels:

| Intent | What happens | Typical use |
| --- | --- | --- |
| **Stop using this in the agent** (default) | Matching memories are **expired** - they no longer appear in recall, query, profile, or chat context, but remain stored for audit and point-in-time review. | “Forget my old job”, entity delete, routine privacy tidying. |
| **Remove it for compliance** (explicit opt-in) | The same match is **permanently erased**, including correction history for the affected entities. | Right-to-erasure requests, retention policy enforcement. |
| **Erase a whole scope subtree** | Everything tagged under a scope path is **hard-deleted** across memory and knowledge. | GDPR-style deletion for a user or team branch. |

Default forget and entity delete are **soft**: the agent behaves as if the information is gone, while operators can still inspect what was retired. Compliance paths require an explicit **`purge`** flag on **`POST /forget`**, or **`POST /scopes/forget`** for subtree erasure - both need the **`memory:forget`** grant on the relevant scope. Audit traces may still record that a forget operation ran, without retaining the forgotten content itself.

See [Forgetting memories](/docs/agent-memory/operations/forget.md) for request shapes and CLI equivalents.
