# Conflicts and summaries

> How MMW surfaces disagreement between versions of a fact, and why a summary never hides unresolved conflicts.

On this page you will learn:

1. when a record gets the `conflict` status and why MMW never silently overwrites anything;
2. how to write a summary with `derived_from` so it does not hide contradictions;
3. how to resolve a conflict.

Running example: on Monday Anna's agent saved that staging is deployed through the `app-staging` systemd unit. On Thursday Ben's agent saved that staging is now deployed with Docker Compose. Deciding who is right is a job for people, not for memory.

## When a conflict appears

Records are compared by `fact_key`. If one workspace holds several records with the same key but different **content or source hash**, all of them get the `conflict` status.

```text
  workspace default, fact_key = deploy.staging.method
  ────────────────────────────────────────────────────────────────
  A  Mon  "through the app-staging systemd unit"        → conflict
  B  Thu  "through Docker Compose, staging service"     → conflict
```

- **Nothing is overwritten.** `remember` always adds a new record, even with the same key. The older version stays and is flagged.
- **Both sides are flagged.** Not only the older record but also the newer one: the server does not know which is right.
- **Newest version first.** In `search`, the newest record with a key ranks above older ones, even when an older one matches the query better. If only an older version matches, the newest is pulled into the results next to it. Older versions carry `superseded_by` with the newest record's ID.
- **Identical text is not a conflict.** Saving the same wording again with the same source hash creates no disagreement.
- **Same text, new hash is a conflict.** If the document changed and you re-saved the fact with a new `source_hash` without removing the old one, the versions differ by hash. Remove the old one first — see [Source and freshness](/memory/source-and-freshness/).
- **Boundaries.** Only records in the same workspace that you can see are compared. Records without a `fact_key` never conflict.

## What the agent sees

```json title="search: 'how is staging deployed'"
[
  {
    "id": "b3e1…",
    "content": "staging is deployed with Docker Compose, staging service",
    "fact_key": "deploy.staging.method",
    "status": "conflict",
    "created_at": "2026-10-08T14:03:00+00:00"
  },
  {
    "id": "a7d2…",
    "content": "staging is deployed through the app-staging systemd unit",
    "fact_key": "deploy.staging.method",
    "status": "conflict",
    "superseded_by": "b3e1…",
    "created_at": "2026-10-05T09:12:00+00:00"
  }
]
```

The newest version is a hint, not a verdict. When a record was saved as a correction, `superseded_by` shows it; when in doubt, the right reaction is not to pick a version by date but to tell the person: "Memory holds two contradicting facts about deploying staging, from October 5 and October 8. Which one is current?"

## Summaries: derived_from

Over time records pile up and the agent condenses them into a summary. To keep the summary from becoming a "clean" version that has lost its contradictions, pass the IDs of the source records in `derived_from`:

```json title="remember"
{
  "content": "Deployment summary for the week: staging is moving to Docker Compose; migrations run with make migrate before deploy",
  "fact_key": "deploy.weekly-summary.2026-w41",
  "derived_from": ["a7d2…", "b3e1…", "c9f0…"]
}
```

- `derived_from` takes up to 50 IDs, and only of existing records you can see; otherwise you get `derived_from contains unknown memory ids`;
- in `search` results, the summary carries a `derived_from` field listing its sources;
- **a summary inherits the worst status** of its sources: if any of them is `conflict` or `stale`, so is the summary. Inheritance goes up to three levels deep, so a summary of summaries still shows the problem.

In the example, the summary gets `conflict` even though its own text disagrees with nothing: the contradiction underneath is unresolved.

## Resolving a conflict

1. **Find out which version is right.** Ask a person or re-read the source. Do not go by date: a newer record is not necessarily correct.

2. **Delete the wrong version** with `forget` by its ID:

   ```json title="forget"
   { "memory_id": "a7d2…", "workspace": "default" }
   ```

   Deletion is soft: the record disappears from search and the graph, and the fact of deletion is kept for audit.

3. **Check the result.** In the next `search`, record B is `unverified`: the key now has a single version.

4. **Update the summary if needed.** Deleted sources no longer affect a summary's status, so if its other sources are fine it also becomes `unverified`. But its text may have relied on the wrong version — if so, save a new summary and delete the old one.

Do not "resolve" a conflict by saving a third record with the same key: that is just a third version, and all three will be `conflict`. The conflict goes away only when the key is left with one version of the content.

## Conflicts and the archivist

The [archivist](/memory/archivist/) works alongside, but differently. It may notice that a new record contradicts a recent one even without a shared `fact_key`, and mark the older one `stale` while lowering its confidence. Nothing is deleted. A `fact_key` conflict is a strict server rule; archivist marks are a model's judgement.

## Where next
