Agent Memory and State Patterns for Loops
Swapnil's Substack
SubscribeSign in
Agent Memory and State Patterns for Loops<br>Why Your Agent Keeps Making the Same Mistake
Swapnil Talekar<br>Jul 23, 2026
Share
In my first post on loop engineering, I covered five building blocks: automations, worktrees, skills, connectors, and sub-agents. I also mentioned a sixth thing that Addy Osmani calls the spine of the whole system, which is memory. I left it there with a single line and a promise to come back to it. This is that post.<br>Memory is the building block that decides whether a loop actually gets better over time or just repeats the same mistake on a schedule. A loop without memory runs fine every single time, and every single time it starts from zero, re-reading the codebase, re-deriving conclusions it already reached, and sometimes retrying the exact fix that failed yesterday because nothing told it that fix already failed.<br>Thanks for reading Swapnil's Substack! Subscribe for free to receive new posts and support my work.
Subscribe
That’s the failure mode this post is about, not a crash or an error, but a loop that works, technically, while quietly wasting a share of every run relearning what it already knew.
What memory actually is in a loop
People conflate memory with two other things, so it’s worth being precise about what it isn’t before getting into what it is.<br>Memory is not the context window. A Claude Code session holds a conversation in its context window while it’s running, and that context disappears the moment the session ends. If a loop kicks off a fresh session every morning, that session has no idea what happened yesterday unless something outside the session told it. The context window is short-term and session-scoped, and memory has to survive past that.<br>Memory is not a skill either. A SKILL.md file holds project conventions: how to run tests, how PR titles should be formatted, which files are off-limits. That information is static. It doesn’t change from one loop run to the next, which is exactly why it belongs in a skill and gets loaded fresh every time. Memory is the opposite. It’s the part that does change every run, because it’s a record of what actually happened: which issues got triaged, which fix attempt failed and why, what the loop is currently waiting on. A skill tells the loop how to work. Memory tells it what’s already been done.<br>In practice, four things need to persist across runs: the decisions the loop made and the reasoning behind them, so a later run or a human reviewing the log understands why the code looks the way it does; what failed and why, so the loop doesn’t burn a turn retrying an approach that’s already been ruled out; the current status against whatever goal the loop is working toward, so an interrupted run can resume instead of restarting; and handoff notes for whoever picks this up next, whether that’s another agent or a person reading the standup summary the next morning.
Three memory patterns, with a real example
The simplest pattern, and the one worth starting with by default, is a flat markdown file. In the GitHub triage loop from my first post, the triage agent wrote its classifications to a triage.md file, and issues that failed review got logged to flagged.md. That’s memory in its most basic form, a plain text file that any agent can read and write, that a human can open and understand without any tooling, and that lives in git so changes to it are tracked the same way code changes are. For a solo project or a small team, this is usually enough, and it’s the pattern I’d default to.<br>One practical question comes up immediately with this pattern: does the memory file belong in the main branch alongside the code, or somewhere separate? Committing it to main means every commit that touches code can also show what the loop was thinking at the time, useful for a human reviewing history later. The tradeoff is that a fast-growing memory file adds noise to the commit log for a directory nobody but the loop reads day to day. A middle ground that works well in practice is keeping the memory file in its own directory, committed but excluded from normal code review, so it’s tracked and diffable without cluttering pull requests built around actual code changes.<br>The second pattern is a task tracker, meaning Linear, GitHub Issues, or something similar. This is worth reaching for when the state needs to be visible to humans and other agents at the same time, and when the workflow already has native states worth plugging into, like open, in review, or blocked. If the fix agent from the triage loop opened a PR and linked it to a GitHub issue, the issue itself becomes part of the memory. It carries status, comments, and history that both people and agents can read without any of that structure being built by hand.<br>The third pattern is a structured store, either a database or a vector store for semantic recall. This is the heaviest option, and it’s only worth the added...