# Persistent memory

A spec-driven workflow only pays off if intent survives. The decision you made
in `clarify` last Tuesday, the reason a domain boundary sits where it does, the
bug that taught you to never retry a non-idempotent call — none of that can live
only in a chat transcript that vanishes at the next context compaction. Forge
gives every project a **durable memory** so that context accumulates instead of
evaporating.

That memory is backed by **Engram**, a spec-memory store. Forge wires Engram
into your configured agents and exposes it through first-class commands; Engram
owns the store itself. This page explains what gets remembered, how it fills up
across the SDD spine, how a team shares it, and exactly which Forge commands
touch it.

## What Forge wires vs. what Engram owns

The boundary is deliberate and worth stating up front, because it explains why
Forge never invents a memory schema.

| Concern | Owner |
| --- | --- |
| Wiring the memory backend into each configured agent | **Forge** |
| Exposing memory through `forge memory` and `forge recall` | **Forge** |
| Populating memory across the SDD phases | **Forge** phase instructions |
| Storing observations and versioning them for sync | **Engram** |
| Search, retrieval, and project detection | **Engram** |

```mermaid
flowchart LR
  Phase["SDD phase / agent"] -->|save + search| Forge["forge memory / recall"]
  Forge -->|MCP tools| Engram["Engram backend"]
  Engram --> Disk[".forge/memory/ export"]
  Disk -->|commit| Git["Git — shared with the team"]
```

::: info Forge is not in the model's inference loop
Memory is advisory context, not a gate. Forge surfaces prior decisions to inform
`explore` and `specify`; it never blocks a change on what memory does or doesn't
contain. The enforced ledger bounds runaway loops and spend — memory bounds
nothing. It only makes the model better informed.
:::

## What memory remembers

The unit of memory is the **observation** — a discrete, retrievable fact
distilled from the work. A save writes one; a search or recall reads them back by
relevance. An observation is not a chat log. It's the distilled residue of a
decision — "auth tokens are rotated every 15 minutes because of the AZ-failover
requirement", "the payments domain owns idempotency keys, not the API layer" —
the kind of thing you'd otherwise re-derive from scratch three sprints later.

The observations worth keeping fall into a few recurring kinds:

| Kind | What it captures |
| --- | --- |
| **Decision** | A choice made and the constraint that forced it |
| **Discovery** | Something the codebase turned out to be doing |
| **Bug fix** | A trap and the reason it bites, so it bites once |
| **Convention** | A rule the project follows that no spec spells out |

You interact with observations at the concept level through three operations —
save, search, and pull-into-context — and never touch Engram's internal storage
directly.

## How memory fills up across the spine

You do not manage memory by hand for the common case. As each SDD phase runs,
Forge's phase instructions have the agent save what it learned and recall what's
relevant before it acts. Memory accumulates as a side effect of doing the work.

```mermaid
flowchart LR
  E[explore] -->|recall prior<br/>decisions| M[(Engram)]
  S[specify] -->|save intent<br/>+ open questions| M
  C[clarify] -->|save resolved<br/>ambiguities| M
  I[implement] -->|save decisions<br/>+ bug fixes| M
  V[verify] -->|save outcomes| M
  A[archive] -->|save the<br/>change summary| M
  M -.->|informs next change| E
```

- **`explore`** opens by recalling prior decisions relevant to the change's
  intent, so investigation starts from accumulated context rather than a blank
  slate.
- **`specify` and `clarify`** save the intent and the resolutions to open
  questions, so the *why* behind each requirement is retrievable, not just the
  *what*.
- **`implement`** saves the decisions and bug fixes made while writing code —
  the details that never make it into a spec but cost hours to rediscover.
- **`archive`** saves a summary of the finished change, closing the loop so the
  next `explore` inherits it.

::: tip Automated saves stay quiet
Saves that come from SDD phase artifacts — proposal, spec, design, tasks,
verify, archive reports — are marked as automated so they don't drown out the
deliberate, human-intent saves. Recall surfaces the signal, not the machine
chatter.
:::

## Project detection keeps memory scoped

Memory is grouped by **project**, and Engram detects the project automatically —
it reads your Git remote URL at startup, normalizes it, and uses that as the
project name. Outside a Git repo it falls back to the directory name. You never
label a project by hand, and the same repository stops accumulating memories
under three slightly different names.

::: warning One remote, one project
Because detection keys on the normalized Git remote, forks or renamed remotes
can drift into a second project name. If recall comes back thinner than
expected, check that you're on the remote whose memory you expect — consolidating
split project names is a supported repair, not a re-derivation.
:::

## The commands that touch memory

Three surfaces reach the store. Use whichever fits the moment — they hit the
same Engram backend.

| Surface | What it does |
| --- | --- |
| `forge memory save\|search\|context\|sync` | The direct CLI: save an observation, search by keyword, pull recent session context, or export for sharing |
| `forge recall` | Advisory recall — surface prior specs and decisions relevant to the current change's intent, to inform `explore` and `specify` |

For the exact subcommands and flags, see [Commands](/guide/commands). `forge
recall` is intentionally read-only and non-blocking: it feeds context in, never
gates a change.

```text
# save a deliberate decision
$ forge memory save "payments domain owns idempotency keys"   # exit 0

# search before you re-derive something
$ forge memory search "idempotency"                           # exit 0

# pull it into a new change's exploration
$ forge recall                                                # exit 0
```

## Team sharing by committing memory

Memory is local by default. To share the accumulated context of a project with
your team, you **export it and commit that export to Git**. `forge memory sync`
writes the shareable memory to `.forge/memory/`, and from there it is just files
in your tree.

```text
# after a work session, export memories into .forge/memory/
$ forge memory sync                                           # exit 0

# then commit them like any other project file
$ git add .forge/memory/ && git commit -m "share project memory"   # exit 0
```

A teammate who clones the repo gets that memory with it and starts with the full,
accumulated context of the project — every decision, convention, and hard-won
bug fix the team recorded. This is where memory pays off most: **onboarding**.
A new contributor doesn't begin cold; they begin with the team's collective
understanding already loaded, and the SDD spine keeps adding to it from their
very first change.

::: info Memory is a project file, not a service
There is no hosted memory, nothing it stores is uploaded, and there is no
external dependency. Shared memory
is just files you diff, review, and commit — which means it inherits your
existing access controls, review process, and history for free.
:::

## Next steps

- [Spec-driven development](/concepts/methodology) — why durable intent is the point of the whole method.
- [The workflow](/guide/workflow) — the spine whose phases populate memory.
- [Commands](/guide/commands) — the full `forge memory` and `forge recall` surface.
- [The enforcement doctrine](/harness/overview) — what Forge enforces, and why memory stays advisory.
