# Remember & Forget

The small-text memory fast path. `remember` stores one fact, preference, decision or note **synchronously**, and the memory is searchable — by keyword and by meaning — the moment the call returns. `forget` removes a memory reversibly, with a preview of what it will touch. Both are one call in the SDKs, one tool in the MCP server, and two routes on the API.

Use this path for the things an agent learns turn by turn: "the deploy target is fly app mg-prod", "the user prefers dark mode", "we decided to ship the migration first". Use `/ingest/document` for anything longer than a few paragraphs (extraction, chunking and citations run there), and the typed cognitive endpoints when the shape matters (a claim with evidence, a goal, a decision).

## Remember

```typescript
const memory = await graph.remember("Deploy target is fly app mg-prod", {
  custom_id: "deploy:target",   // stable key → re-sends update this memory
})
// memory.action      "inserted" | "updated" | "unchanged" | "reused"
// memory.searchable  { bm25: true, vector: true }
```

```python
memory = graph.remember("Deploy target is fly app mg-prod", custom_id="deploy:target")
```

```bash
curl -X POST https://api.mindgraph.cloud/memory/remember \
  -H "Authorization: Bearer mg_..." \
  -H "Content-Type: application/json" \
  -d '{"text": "Deploy target is fly app mg-prod", "custom_id": "deploy:target"}'
```

MCP: `mindgraph_remember {"action":"remember","text":"...","custom_id":"deploy:target"}`.

### Request

| Field | Type | Meaning |
|---|---|---|
| `text` | string, required | The memory. At most 8 KiB; longer content belongs in `/ingest/document`. |
| `custom_id` | string | Caller-owned key (≤ 512 bytes). Re-sending the same key with new text **updates the same node** (version bump, history kept); with identical text it is a no-op (`action: "unchanged"`). This is the idempotency contract — there is no separate idempotency key. |
| `label` | string | Short title; defaults to the first line of `text` (80 characters). |
| `space_uid` | string | Explicit target Space; default is the org space. The principal must be able to write there. |
| `on_near_duplicate` | `"reuse"` (default) or `"create"` | Without a `custom_id`, what to do when write-time resolution finds an exact duplicate: return the existing memory, or insert anyway. |
| `props`, `confidence`, `salience`, `agent_id` | | As on `/reality/capture`. The node is an `Observation` with `observation_type: "memory"`. |

### Response

```json
{
  "uid": "obs_…",
  "custom_id": "deploy:target",
  "action": "inserted",
  "label": "Deploy target is fly app mg-prod",
  "version": 1,
  "space_uid": "space:org",
  "embedded": true,
  "searchable": { "bm25": true, "vector": true },
  "near_duplicates": [],
  "remember_instructions": null
}
```

`201 Created` for `inserted`, `200 OK` otherwise.

**Searchable on return, honestly.** Keyword search (BM25 over label, summary and search text) is always live when the call returns. The embedding is computed inside the request with a bounded timeout (3 seconds by default; `MINDGRAPH_REMEMBER_EMBED_TIMEOUT_MS` on self-hosted servers) and stored before the response, so vector search is live too — and the response says so in `searchable.vector`. If the embedding provider is slow or down, the memory is still written, the normal background embed picks it up, and the response reports `embedded: false` rather than claiming readiness it does not have.

**Duplicates.** With a `custom_id`, the key decides: same key, same node. Without one, MindGraph's write-time resolution runs; an exact hit is reused (`action: "reused"`) unless you pass `on_near_duplicate: "create"`. Near hits are returned in `near_duplicates` for you to look at, never acted on silently.

## Forget

```typescript
// Preview first: which edges would go?
const preview = await graph.forget({ custom_id: "deploy:target" }, { dry_run: true })
// preview.edge_uids, preview.would_tombstone_edges

// Then do it (reversible)
const done = await graph.forget({ custom_id: "deploy:target" })
```

```python
preview = graph.forget(custom_id="deploy:target", dry_run=True)
done = graph.forget(custom_id="deploy:target")
```

```bash
curl -X POST https://api.mindgraph.cloud/memory/forget \
  -H "Authorization: Bearer mg_..." \
  -H "Content-Type: application/json" \
  -d '{"custom_id": "deploy:target", "dry_run": true}'
```

MCP: `mindgraph_remember {"action":"forget","custom_id":"deploy:target","dry_run":true}`.

| Field | Meaning |
|---|---|
| `uid` or `custom_id` | Which memory. `uid` wins if both are given. |
| `dry_run` | `true` reports what would be tombstoned and changes nothing. |
| `cascade` | Also tombstone the memory's connected edges (default `true`). |
| `reason` | Recorded on the tombstone. |

`forget` is a **tombstone**, not a delete: the node and its edges stay in history and can be brought back. The response (and the preview) lists every affected `edge_uid`, and an `undo` block tells you how: `/evolve action=restore` for the node, then `/evolve action=restore_edge` per edge uid. Forgetting an already-forgotten memory is a `404 already_forgotten`, not a second tombstone.

Scopes: `remember` needs the `write` scope; `forget` needs `evolve`, like the other lifecycle mutations. Neither consumes credits.

## Remember instructions

Operators can tell agents what is worth remembering. The text is stored on the Space (org space by default), returned on every `remember` response as `remember_instructions`, and included at the top level of `/retrieve/context` so an agent sees it whenever it orients. MindGraph stores and surfaces it; it does not interpret it.

```typescript
await graph.setRememberInstructions(
  "Remember decisions, deploy targets and stated preferences. Skip greetings and status chatter.",
)
const { remember_instructions } = await graph.getRememberInstructions()
```

```python
graph.set_remember_instructions("Remember decisions, deploy targets and stated preferences.")
graph.get_remember_instructions()
```

```bash
curl -X POST https://api.mindgraph.cloud/memory/config \
  -H "Authorization: Bearer mg_..." \
  -H "Content-Type: application/json" \
  -d '{"action": "set_remember_instructions", "text": "Remember decisions and deploy targets."}'
```

An empty `text` clears it. At most 4 KiB.

## How it compares with the other write paths

| You want to store… | Use |
|---|---|
| One fact, preference, decision or note, right now, findable immediately | `remember` |
| A document, transcript or long note with extraction, chunking and citations | `/ingest/document`, `/ingest/session`, `/ingest/chunk` |
| A typed thing — a person or organization, a claim with evidence, a goal, a decision | `/reality/entity`, `/epistemic/argument`, `/intent/commitment`, `/intent/deliberation` |
| A session's journal entry or trace, attributed to that session | `/memory/session` (`journal`, `trace`) |

All of them land in the same graph with the same provenance, versions and access rules; `remember` is simply the shortest path in.
