Memory and state for long-running automations
Split what a multi-day automation remembers into working state, durable state and recall, and decide what each keeps, where, and for how long.
On this page
An automation that runs for three days is not running for three days. It runs for a few seconds at a time, when an application arrives, when the founder approves, when a candidate books, and in between it is a row in a database. What that row holds, where it lives and when it is deleted is the design, and the model's context window is not where any of it lives.
The mistake: one conversation for the whole role
The recruiting screen from one idea, two skins handles about 150 applicants a role. The quick build is one agent conversation per role: paste each CV in as it arrives and tell the model to remember who it has scored, so that on day three it can answer "did we invite Priya Shah?"
The arithmetic, on the course's assumptions. A CV and cover note of about 1,100 words is 1,430 tokens at 1.3 tokens a word; with the form and the rule results, about 1,700 tokens of input per applicant. Add the model's scores and its calls and each applicant grows the conversation by about 2,000 tokens. The system prompt, rubric and job description are 2,000 more, resent every turn, and 8,000 is reserved for output in a 200k window.
room for applicants 200,000 − 2,000 − 8,000 = 190,000 tokens
applicants that fit 190,000 ÷ 2,000 = 95
Applicant 96 of 150 does not fit, nor do the 54 after it. Before that, each new call re-reads everything before it: the k-th applicant's call reads about 2,000 + 2,000k tokens, so scoring 95 applicants reads about 9.3 million input tokens, roughly $18.60 at the assumed $2 per million. One thread per applicant reads each CV once, about 3,700 tokens in and 250 out: 150 × 3,700 × $2 ÷ 1M + 150 × 250 × $8 ÷ 1M ≈ $1.11 + $0.30 = $1.41 for all 150.
The answer to "did we invite her?" is worse than expensive. The fact sits forty applicants deep in a long context, where Liu et al. measured models finding things less reliably ("Lost in the Middle (opens in a new tab)"). It has an exact answer in a row, if a row exists. Compaction, context editing and scratchpads, the ways to keep one run's window small, are in memory and context management. None of them turns a window into a database.
Three kinds of state
Working state is what one step needs while it runs: the CV text, the model call and its raw reply, a tool result. It lives in the step's memory for seconds and is discarded when the step returns its update. Nothing in it survives unless the step's output names it.
Durable state is what this applicant's run has established: the fields in the screen's state block, saved as a checkpoint after every step, one thread per applicant. A restart resumes from it, and so does the founder's click. In LangGraph it is the checkpointer's thread (Persistence (opens in a new tab)); on an n8n canvas it is the waiting execution's saved data (Wait (opens in a new tab)).
Recall is what happened before, across threads. Has this person applied before, and what was decided? Which of the model's totals did the founder override last time? It lives in a store keyed by something exact and is read by a code step before scoring. LangGraph calls this a store, organised by namespaces (Memory overview (opens in a new tab)); in any skin it is a table with a key.
What to persist, where, for how long
The privacy notice the agency gives applicants sets the outer limit; assume it promises deletion six months after the role closes.
| Holds | Kind | Lives in | Kept until |
|---|---|---|---|
| the CV text in a prompt, the model's raw reply | working | the step | the step returns |
| the CV file | input | file store, referenced by id | close + 6 months |
| rule results, scores, quoted evidence, total, route | durable | checkpoint | close + 6 months |
| the founder's verdict, and when | durable | checkpoint | close + 6 months |
| invite key, sent at, deadline, reminder sent | durable | checkpoint | the role closes |
| earlier applications and their outcomes | recall | store, keyed by a hash of the email | 6 months after each role |
| the founder's overrides of the model's total | recall | store, per rubric version | the next rubric change |
Two rules generate the table. Store a reference, never a copy: the CV lives in one place, so deletion is one delete, not one per checkpoint. Persist decisions, not deliberation: the checkpoint keeps five scores and five quoted lines, not the model's reasoning or the prompt that produced them, because nothing downstream reads them and every copy is personal data to find on the deletion date.
Storage is not the argument. About 2 KB of fields a checkpoint, eight checkpoints an applicant, is 16 KB; a role of 150 is 2.4 MB. Copying the CV and the transcript into every checkpoint makes it about 14 MB, still nothing to a database, and 1,200 copies of personal data to delete.
Checkpoints and threads across three days
Follow one applicant, thread role-14/app-0093, who applies at 17:00 on day one, after the founder's 16:00 sitting:
- Day 1, 17:00. Rules, scoring and routing run in about 6 seconds, with a checkpoint after each. The total is 15, so the thread stops at the interrupt.
- Day 2, 09:00. The founder approves in her morning sitting. The thread resumes from its checkpoint, the invite goes with the key
role-14/app-0093/invite, and the thread stops again, waiting for the booking, withdeadlineset 48 hours after the send. - Day 3, 09:00. The reminder fires, 24 hours before
deadline, from a timer read out of state rather than a process that slept. - Day 3, 11:30. The candidate books; the thread records the slot and ends.
If the server restarts at 03:00 on day two, every thread waiting on the founder is a row, and every timer re-arms from deadline in state. A step that was mid-flight reruns from its last checkpoint, which is why the invite carries a key: with idempotency, the rerun's send finds the first and does nothing.
What the model sees
Each model call is assembled by code from durable state and recall: the rubric, the job description, this CV, and at most one line of recall, such as "applied in March, declined at review". No other applicant is in it. The model never searches memory; a code step reads the store by key and hands over the field. A question that needs a search, such as "candidates like the designer we hired last year", is a retrieval feature with its own evaluation, not memory the screen depends on.
In practice
Ask the builder four questions. Where does an applicant's state live while the founder is away for a day? What survives a restart? Which copies of an applicant's data exist, and what deletes each one on the retention date? What is the model sent on each call, field by field?
The mistake that costs money is treating the context window as the memory. One conversation per role fails at applicant 96, spends about $18.60 of input getting there against $1.41 for all 150 in threads, and answers "did we invite her?" from a transcript instead of a row.
Check yourself
1. A senior role draws 400 applicants instead of 150. What does the threaded design cost in tokens and storage, and what in the design changes?
400 × 3,700 = 1.48M input, $2.96; 400 × 250 = 100,000 output, $0.80; about $3.76 for the role. Storage is 400 × 16 KB = 6.4 MB. Nothing in the design changes. The number to watch is the founder's queue, which grows with the band of 12 to 17.
2. The founder wants the screen to "remember" that she prefers candidates with agency experience. Where does that go?
In the rubric, not in memory. A preference about how to score is a rule: it becomes a sixth criterion or a change to one, versioned, with the version written into every thread. Undecided threads scored under the old version are re-scored; decided ones keep their version, so a difference in totals can be explained. A preference kept in recall or in a conversation is applied to some applicants and not to others.
3. The mail service accepted the invite, then the worker crashed before the checkpoint recorded invite_sent_at. On resume, what happens, and what makes it safe?
The send step reruns from the last checkpoint. Without a key, the candidate gets two invites. With the send keyed on
role-14/app-0093/invite, and the mail step refusing a key it has already sent, the rerun does nothing. The checkpoint cannot close this gap alone, because the crash fell between the side effect and the save.