# State Persistence

Course: [AI Agents](https://www.devtutorials.ai/learn/ai-agents) · Section: Memory Management
Source: https://www.devtutorials.ai/learn/ai-agents/memory/state-persistence

> How an agent saves its progress so it survives a crash: serialization, snapshots, recovery points, restoring, integrity checks and drift.

Think about a long video game.

You have been playing for two hours when the power suddenly goes out.

If you never saved your progress, you have to start again from the beginning. If you saved at the last checkpoint, you can continue from there and lose only a small amount of progress.

Now imagine that someone else used your save file and spent all your coins while you were away.

If the game loads that save without checking it, your notes might say "500 coins" while the game actually has 0. You would make plans based on information that is no longer true.

A well-designed game saves progress regularly and validates saved data before trusting it.

An agent needs the same two habits.

**The main idea:** State persistence means saving an agent's progress so it can recover after a crash. A good saved state is easy to restore, stored regularly, and validated before it is used.

## A Real-World Example

The Brightgrid agent has already run the tests, read `web/format.js`, and written a fix. Then the browser tab crashes.

The fix is safe because it has already been written to the file. However, the agent's **memory** is gone. Its memory contained information such as which test failed, what files it read, and what changes it made.

Without a saved state, the agent must start again from the beginning. With a saved state, it can reload its previous steps, verify that the project still matches what it expects, and continue directly with "run the tests again".

**In simple words:** Save progress regularly and verify it before trusting it.

## What You Will Learn

In this lesson, you will learn:

1. How to convert an agent's state into text and back again through serialization.
2. What a snapshot is and when it should be created.
3. What recovery points are and why they matter.
4. How to restore an agent from a saved snapshot.
5. How an integrity check detects a corrupted or outdated save.
6. How to handle drift when the world changes while the agent is not running.

The **Agent Lab** includes three crash scenarios. In each one, the process stops after step 3. Compare what happens with no save, with a valid save, and with an outdated save. The **Memory** tab shows the size and fingerprint of each snapshot.

## State Saving

### State Serialization

**Serialization** converts data in memory into text so it can be stored or transferred. **Deserialization** converts that text back into data. In JavaScript, `JSON.stringify` performs serialization and `JSON.parse` performs deserialization.

Be careful because not every data type survives serialization correctly. A `Set` or a `Map` becomes an empty object `{}`. Plain objects, arrays, strings, numbers, and boolean values are safe to serialize.

```js runnable title="What survives JSON and what doesn't"
const state = {
  goal: "Make every test pass",
  steps: [{ n: 1, tool: "run_tests" }],
  readFiles: new Set(["web/format.js"]),
};

const text = JSON.stringify(state);
const back = JSON.parse(text);

console.log(text);
console.log("steps kept: " + back.steps.length);
console.log("readFiles kept: " + JSON.stringify(back.readFiles));
```

Expected output:

```text
{"goal":"Make every test pass","steps":[{"n":1,"tool":"run_tests"}],"readFiles":{}}
steps kept: 1
readFiles kept: {}
```

The `Set` becomes empty when it is restored. The agent would lose track of which files it has already read. Instead, store it as an array using `[...readFiles]`.

### Snapshot Creation

A **snapshot** is a saved copy of the agent's state at a specific point in time. A good snapshot contains:

- a **version** number, so older saves can be identified
- the **goal**
- the **step** number when the snapshot was created
- the **steps** completed so far (the agent's memory)
- a **hash** of the project files

A **hash** is a short fingerprint of some text. If even a single character changes, the fingerprint changes as well. You will use this fingerprint later during the integrity check.

```js runnable title="A tiny hash: one character changes everything"
function hash(text) {
  let h = 5381;
  for (let i = 0; i < text.length; i++) h = ((h * 33) ^ text.charCodeAt(i)) >>> 0;
  return h.toString(16);
}

console.log(hash('return n + " rows";'));
console.log(hash('return n + " rows";'));
console.log(hash('return n + " row";'));
```

Expected output:

```text
b4d6ac48
b4d6ac48
bfa9707b
```

The same text always produces the same hash. Removing even one letter creates a completely different hash.

### Recovery Points

A **recovery point** is a moment that the agent can safely return to after a failure. The Agent Lab creates a snapshot after *every* step. If a crash happens, the agent loses at most one step of work.

Saving more often adds a small amount of overhead. Saving less often increases the amount of work that could be lost after a crash.

```js runnable title="How much work does a crash lose?"
function lostSteps(saveEvery, crashAfter) {
  const lastSave = Math.floor(crashAfter / saveEvery) * saveEvery;
  return crashAfter - lastSave;
}

for (const every of [1, 3, 5]) console.log("save every " + every + " steps, crash after step 7: lose " + lostSteps(every, 7));
```

Expected output:

```text
save every 1 steps, crash after step 7: lose 0
save every 3 steps, crash after step 7: lose 1
save every 5 steps, crash after step 7: lose 2
```

Saving after every step means the most recently completed step is always safe.

The agent writes the fix at step 3, and then the process crashes.

Because nothing was saved, the run ends with an error. The fix still exists in the file, but there is no record showing whether the fix was checked.

## State Recovery

### State Restoration

**State restoration** means loading a snapshot back into memory. After restoring the snapshot, the agent continues from the next step as if the interruption never happened.

### Integrity Checks

An **integrity check** asks a simple question: can this snapshot be trusted?

It checks three things:

1. Can the snapshot be read? (Is it valid JSON?)
2. Is it a version the agent understands? (The version number.)
3. Does it still match the current state of the world? (Does the file hash still match the current files?)

```js runnable title="Check a snapshot before trusting it"
function hash(text) {
  let h = 5381;
  for (let i = 0; i < text.length; i++) h = ((h * 33) ^ text.charCodeAt(i)) >>> 0;
  return h.toString(16);
}

function check(snapText, filesNow) {
  let snap;
  try { snap = JSON.parse(snapText); } catch { return "REJECT: not valid JSON"; }
  if (snap.version !== 1) return "REJECT: unknown version " + snap.version;
  if (snap.hash !== hash(filesNow)) return "REJECT: files changed since the save";
  return "OK: restore from step " + snap.step;
}

const files = 'return n.toLocaleString("en-US") + " rows";';
const good = JSON.stringify({ version: 1, step: 3, hash: hash(files) });

console.log(check(good, files));
console.log(check(good, "// Reviewed by Sam.\n" + files));
console.log(check('{"version":1,"step":3', files));
console.log(check(JSON.stringify({ version: 2, step: 3 }), files));
```

Expected output:

```text
OK: restore from step 3
REJECT: files changed since the save
REJECT: not valid JSON
REJECT: unknown version 2
```

The second file gained only a comment, but that is enough to change the hash. The snapshot no longer matches the current state of the files.

After the crash at step 4, the snapshot passes the integrity check. The agent knows it already wrote the fix, so step 5 runs the tests and step 6 finishes. It completes the task in 6 steps instead of starting over from the beginning.

### Drift Handling

**Drift** means the world changed while the agent was not running. A teammate may have edited a file, a branch may have been merged, or a new test may have been added.

When drift is detected, the agent has two safe options:

1. **Reject the snapshot and take a fresh look.** Run the tests again and reread the files. This is the safest option.
2. **Re-check only the changed parts.** Keep the snapshot, but reread every file whose hash changed. This is faster, but it requires a separate hash for each file.

The unsafe option is to load the old snapshot and continue as if nothing changed.

While the process was down, a teammate added a comment to `web/format.js`. The hash no longer matches, so the snapshot is rejected. The agent clears its saved memory and starts with a fresh test run.

The tests now pass because the fix from step 3 is still present in the file. The agent finishes in 6 steps without trusting outdated state information.

```js runnable title="Find exactly which files drifted"
function hash(text) {
  let h = 5381;
  for (let i = 0; i < text.length; i++) h = ((h * 33) ^ text.charCodeAt(i)) >>> 0;
  return h.toString(16);
}

const saved = { "web/format.js": hash("A"), "web/ticket.html": hash("B"), "api/limits.js": hash("C") };
const now = { "web/format.js": "A2", "web/ticket.html": "B", "api/limits.js": "C" };

const drifted = Object.keys(now).filter((path) => hash(now[path]) !== saved[path]);
console.log("re-read: " + drifted.join(", "));
console.log("still trusted: " + Object.keys(now).filter((p) => !drifted.includes(p)).join(", "));
```

Expected output:

```text
re-read: web/format.js
still trusted: web/ticket.html, api/limits.js
```

A hash for each file tells the agent exactly which files need to be checked again. Everything else in the snapshot can still be trusted.

**Easy Way to Remember:** **save often, check before you load**. A save that has not been checked is only a guess about the past.

Never restore a snapshot without an integrity check. An agent using out-of-date notes can overwrite a teammate's work.

## Common Mistakes

| Mistake | Wrong Idea | Correct Idea | What Goes Wrong |
|---|---|---|---|
| No saves | "Crashes are rare." | Save after every step. | A single crash loses the entire task. |
| Sets in JSON | "JSON saves everything." | Convert a `Set` to an array before saving. | Important state silently disappears after restoration. |
| No version number | "The format will never change." | Include a version in every snapshot. | An old snapshot may be restored incorrectly. |
| No integrity check | "The save is ours, so it must be safe." | Verify the hash before restoring. | The agent works from outdated information. |
| Ignoring drift | "Nobody changed the files." | Compare file hashes after restarting. | The agent overwrites a teammate's changes. |

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- Save often, check before you load.
- Serialize using plain data types. A `Set` or `Map` should be converted to an array before saving.
- A good snapshot includes a version number, the goal, the current step, the recorded steps, and a file hash.
- An integrity check catches invalid data, unsupported versions, and drift between the snapshot and the current files.
- When drift is detected, either reject the snapshot or re-read the files that changed before continuing.

Next: **Errors and Fallbacks**, how an agent notices a fault, recovers, and never leaves you with nothing.
