DEV Community

Cover image for Why Does Your Coding Agent Keep Forgetting Things?
Tony
Tony

Posted on

Why Does Your Coding Agent Keep Forgetting Things?

I'm Tony. I make and run web products, and Claude Code is my main development tool every day. A coding agent can follow an architectural decision correctly in one session, then approach the same component differently in another. Say your API requires organization-scoped permissions, but a new session implements authorization using a user's global role. The code compiles, the endpoint responds, and the change still violates a decision your team already made.

The immediate temptation is to preserve more conversation history or give the agent a larger collection of notes. But keeping an entire conversation is not the same as preserving useful knowledge, and remembering an old decision can be worse than forgetting it. In this article, I'll show you how I separate working context, durable project decisions, session handoffs, and information that should be discarded. You'll leave with a small set of reusable templates and a recovery test you can run before letting an agent continue unfinished work.

This is the third article in Making Coding Agents Reliable, a six-article series about making agent-generated code understandable, verifiable, and maintainable. The same principles apply to other coding agents, although the commands and memory behavior below are specific to Claude Code.

For the examples, I'll use a fictional TypeScript monorepo:

.
├── apps/
│   ├── admin/           # Admin web application
│   └── api/             # Multi-tenant API
├── packages/
│   ├── ui/              # Shared UI components
│   └── shared/          # Shared types and contracts
├── docs/
├── CLAUDE.md
└── pnpm-workspace.yaml
Enter fullscreen mode Exit fullscreen mode

The API serves multiple customer organizations and must enforce strict tenant isolation. Our example change introduces organization-scoped permissions for a reporting export feature, affecting database migrations, API authorization, shared types, admin UI, and tests. This is the kind of work where an incorrect remembered assumption can become a security defect.

Context and memory solve different problems

Developers often use "context" and "memory" interchangeably, but they solve different problems. Context is the working information available to the agent as it handles the current request. Memory is information retained for use across sessions. Neither automatically establishes which architectural decision is correct for the current repository.

I think about the available information in three layers:

Layer Contains Main purpose Main risk
Session context Conversation, code read, tool output Complete current work Noise and lost detail
Agent memory Saved project notes and observations Recover useful knowledge across sessions Stale or unverified assumptions
Repository knowledge CLAUDE.md, architecture decisions, task records, tests Share and verify project knowledge Documentation drifting from code

In Claude Code, the conversation history is sent with each request, so continuing a long investigation consumes context. Running tests, reading source files, inspecting logs, and exploring alternative implementations all add material to that working context. You can use /context to inspect what occupies the context window and /usage to inspect token usage. These commands help you understand consumption, but they don't tell you whether the information is still valid.

Claude Code also has auto memory stored under:

~/.claude/projects/<project>/memory/
├── MEMORY.md
└── <topic-files>.md
Enter fullscreen mode Exit fullscreen mode

MEMORY.md is the index, with topic files available when needed. The first 200 lines, or up to 25KB, of MEMORY.md load at session start. You can manage auto memory through /memory. This memory lives on your machine, is shared across worktrees of the same repository, and is not automatically synchronized across developers' machines.

Repository knowledge serves another purpose. A committed architecture decision can be reviewed by teammates, traced through Git history, and changed alongside an implementation. CLAUDE.md loads every session and is useful for stable project instructions, but the rest of your documentation must be explicitly located and read when relevant. I treat repository knowledge as the maintained source of project decisions, then check it against the actual implementation.

Context and knowledge lifecycle

Imagine your agent reads a permission middleware file and finds that authorization depends on user.role. This is current working context because the code exists in the repository. An auto-memory note might say, "Permissions are organization-scoped," while an architecture record explains that the global-role design was superseded. The agent needs to notice the disagreement, inspect the affected code, and establish which parts of the migration are complete before making another change.

The distinction matters because an agent can recall the right architectural principle while operating on the wrong repository state. Successful recovery requires both relevant knowledge and fresh evidence.

Decide what deserves to survive a session

The question I use is not "Will this information be useful again?" Almost anything might be useful again. I ask who will need the information, how long it remains valid, and who is responsible for updating it.

A practical classification looks like this:

Information Example Where I keep it
Stable project rule Use pnpm; enforce tenant isolation CLAUDE.md
Architecture decision Resolve permissions through organization membership Versioned decision record
Task requirement Add organization-scoped report exports Task brief
Work in progress Middleware updated; admin UI pending Session handoff
Verification evidence Test command, result, and revision tested Handoff or verification record
Navigational observation Permission tests live in a particular directory Agent auto memory
Temporary output Full test logs, exploratory searches Current context or disposable artifact

The first two rows deserve deliberate maintenance. If your API must resolve authorization within the requested organization, that rule should not depend on the agent retaining an earlier conversation. It belongs in project instructions and a decision record that describes why the constraint exists. The record should identify the code it affects and the assumptions it replaces.

Progress and test results have shorter useful lifetimes. A passing test result says something about a particular state of the code, not every state that follows it. When authorization middleware changes, yesterday's passing test result does not establish that the new middleware is correct. I preserve the command, outcome, and repository reference so the next session can decide what needs to be rerun.

Temporary observations usually need no preservation. The output from a package installation, a discarded design proposal, or hundreds of successful test lines should not become permanent project knowledge. Keeping them consumes attention and creates more material that a future agent might incorrectly treat as authoritative.

There is also a difference between instruction and evidence. "Always enforce tenant isolation" is an instruction. "The API tests passed" is an evidence claim that needs a test command and a relevant code revision. Storing both as undifferentiated memory makes it harder for the agent to know what to obey and what to verify.

Keep the repository as the source of truth

Consider a typical permission migration. The first design checks authorization through a user's global role. The team then establishes that one user can belong to several organizations and have different roles within each organization. The accepted design changes to evaluating permission through membership in the organization associated with the request.

Both designs may exist in Git history, documentation, and conversations. Without an explicit record, a future session can recover the old design and implement it consistently. The problem isn't that the agent lacks information. It has information without a reliable indication of which decision supersedes which.

For changes that affect several packages, I keep a decision record in the repository. Here is the first file in a small reusable kit:

agent-context-kit/
├── decision-record.md
├── session-handoff.md
└── context-recovery-checklist.md
Enter fullscreen mode Exit fullscreen mode

These are human-managed Markdown documents, not built-in Claude Code memory files. You can keep the templates in your repository and copy completed records into the appropriate architecture or task directories. Claude Code will not automatically discover every document simply because it exists.

agent-context-kit/decision-record.md

# Decision: <short title>

ID: <stable identifier>
Status: Proposed | Accepted | Superseded
Supersedes: <decision ID or none>
Superseded by: <decision ID or none>

## Context
What problem requires a decision?
Which constraints must remain true?

## Decision
What behavior or design is accepted?

## Scope
- Affected packages:
- Affected interfaces:
- Data or security implications:

## Alternatives
Which alternatives were considered?
Why were they rejected?

## Verification
How can we check the implementation?
Which tests or reviews are required?

## References
- Relevant code paths:
- Related decisions:
- Task or change reference:
Enter fullscreen mode Exit fullscreen mode

For our example, the completed decision could live at docs/architecture/decisions/002-organization-membership.md:

# Decision: Organization-scoped authorization

ID: ADR-002
Status: Accepted
Supersedes: ADR-001
Superseded by: None

## Context
Users can belong to multiple organizations.
A user's role may differ between organizations.
The API must not authorize access using a
membership from another organization.

## Decision
Resolve authorization using the authenticated
user's membership in the requested organization.

Check membership and the required permission
before accessing organization-scoped data.

## Scope
- apps/api: authorization and report endpoints
- apps/admin: organization-specific permissions
- packages/shared: permission contracts
- Database: membership and role migration

## Alternatives
Global user roles were rejected because they
cannot represent different organization roles.

## Verification
- Authorized membership permits export.
- Missing membership denies export.
- Permission in organization A does not grant
  access to organization B.
- Export queries remain tenant-scoped.

## References
- ADR-001: Superseded
- Task: docs/tasks/reporting-export/
Enter fullscreen mode Exit fullscreen mode

This record does more than remind the agent of a design preference. It identifies the boundary, the affected components, and the tests that should demonstrate compliance. It also keeps the rejected design visible without presenting it as an active instruction.

The repository isn't automatically correct merely because a Markdown file says "Accepted." The implementation may be incomplete, a migration may have changed behavior, or the document may have been neglected. The repository is the place where the team maintains its decisions and where the agent can inspect current code and tests to challenge them.

Give session handoffs a consistent structure

I use a handoff when work needs to continue across sessions, especially when changes are partially implemented. The handoff isn't an abbreviated transcript. It's a map from the current task to the evidence, decisions, and next useful actions.

A good handoff answers four questions. What are we trying to achieve? What changed in the repository? What has actually been verified? What should the next session inspect before touching code?

agent-context-kit/session-handoff.md

# Session Handoff: <task name>

## Objective
<Desired outcome and acceptance criteria>

## Repository state
- Branch:
- Base commit:
- Uncommitted changes:
- Related task directory:

## Current decisions
- Active decision records:
- Superseded decisions to avoid:

## Completed work
- <File or component>: <what changed>

## Verification evidence
| Command or check | Result | Code state |
|---|---|---|
| <command> | Pass/Fail/Not run | <commit or state> |

## Unfinished work
- <Remaining implementation or validation>

## Known risks and contradictions
- <Unresolved issue>
- <Claim that needs verification>

## Next useful action
1. <First inspection or verification step>
2. <Next action after evidence is reviewed>
Enter fullscreen mode Exit fullscreen mode

For the reporting export change, a handoff might contain:

# Session Handoff: Reporting Export

## Objective
Export reports for the requested organization.
Require membership and export permission.
Preserve strict tenant isolation.

## Repository state
- Branch: feature/reporting-export
- Base commit: <capture actual SHA>
- Uncommitted changes: inspect with git status
- Task: docs/tasks/reporting-export/

## Current decisions
- ADR-002: Organization-scoped authorization
- ADR-001: Superseded global-role design

## Completed work
- packages/shared: permission types updated
- apps/api: membership check added
- Database migration: written, not validated

## Verification evidence
| Check | Result | Code state |
|---|---|---|
| API test suite | Not run | Middleware changed |
| Cross-tenant export | Not run | Current worktree |
| Migration from old schema | Not run | Current worktree |

## Unfinished work
- Complete API middleware validation
- Update apps/admin permission display
- Validate migration with existing memberships

## Known risks and contradictions
- Middleware implementation needs review
- Export query tenant filtering is unverified
- No current cross-tenant test evidence

## Next useful action
1. Compare middleware with ADR-002.
2. Inspect migration and uncommitted changes.
3. Run relevant API tests.
4. Record failures before implementation.
Enter fullscreen mode Exit fullscreen mode

Notice what the handoff does not claim. It doesn't say the migration works just because the migration file exists. It doesn't call the API complete merely because the middleware was modified. It records the difference between implementation progress and verification.

You can ask the agent to prepare this record before ending a working session:

Read the task in docs/tasks/reporting-export/.

Update the session handoff based on the current
repository state and this session's changes.

Include:
- Active architectural decisions
- Files changed and work remaining
- Commands actually executed and their results
- Checks not executed
- Uncommitted changes
- Tenant isolation risks
- The next useful verification step

Do not infer successful tests.
Do not change application code during handoff.
Enter fullscreen mode Exit fullscreen mode

This creates a more useful boundary than asking the agent to "remember everything important." It also gives a reviewer something concrete to correct. If the agent reports a test as passing without evidence, you can fix that claim before it becomes the starting assumption for another session.

Make old knowledge easy to challenge

Stale information is especially troublesome when it sounds like a rule. An auto-memory note saying "authorization uses global roles" can look as decisive as an accepted architecture record unless you establish where each claim came from. The solution is to attach provenance, scope, and validity to important information.

For architecture decisions, I use stable identifiers and explicit statuses. ADR-001 can remain in the repository to explain the original design, but it should be marked Superseded and point to ADR-002. Any active instructions referring to the global-role design must be reviewed. Keeping historical records is useful; keeping conflicting active instructions is not.

Knowledge classification by lifetime and owner

The update process needs to extend beyond the decision file. If CLAUDE.md still instructs the agent to authorize using global roles, the repository contains two conflicting instructions. If a shared permission type still assumes one role per user, the code has not caught up with the decision. If the tests only exercise one organization, they may pass without detecting the missing isolation boundary.

I want the agent to report those disagreements, not silently select whichever source looks most convenient. A useful instruction is:

Before implementing authorization changes:

1. Read the active architecture decision.
2. Inspect the relevant code and tests.
3. Identify conflicting instructions or behavior.
4. Check whether older decisions were superseded.
5. Report disagreements with file references.

Do not resolve contradictions by assumption.
Do not modify code until the conflicts are reviewed.
Enter fullscreen mode Exit fullscreen mode

Auto memory should follow the same principle. Claude Code can preserve useful project observations, but its machine-local notes are not team-approved architecture decisions. I use /memory to inspect that layer and check whether it contains claims that conflict with the repository. I don't use auto memory as the sole location for security constraints, migration requirements, or important design decisions.

The extra care also matters across worktrees. Because worktrees for a repository share auto memory, a remembered observation may describe code from a different branch. The branch, current files, active decisions, and test evidence must establish what applies to the worktree the agent is actually modifying.

Test a fresh-session recovery

A handoff is useful only if another session can recover the task without inventing missing information. I test this by starting with a fresh context and asking the agent to reconstruct the current state before it writes code. The test should expose disagreements between the handoff, architecture records, and working tree.

Claude Code provides several controls for managing session history:

Situation Tool or command
Inspect context consumption /context
Inspect token usage /usage
Summarize a long ongoing conversation /compact
Start fresh for unrelated work /clear
Rename a session before clearing /rename
Return to a previous session /resume
Continue the latest session in the directory claude --continue
Choose a session to resume claude --resume
Restore conversation and code to a checkpoint /rewind

/compact summarizes conversation history, optionally with directions such as /compact Focus on code samples and API usage. Auto-compaction also summarizes older history near the context limit. This reduces context pressure, but a summary can retain a superseded assumption or omit why an alternative was rejected. Compacting a large context is itself a large request, so it shouldn't be treated as a free substitute for maintaining task records.

For a continuous investigation, compaction can retain useful working history. For unrelated work, /clear avoids carrying unnecessary material into the new task and costs nothing. You can rename a session before clearing and resume it if the earlier work needs attention. Returning to a conversation through /resume or claude --continue doesn't establish that the repository is unchanged.

For the recovery exercise, I use a new session with a clear task reference. The first action is inspection, not implementation:

Recover the reporting export task from:

docs/tasks/reporting-export/

Read the task brief, session handoff,
verification evidence, and referenced ADRs.

Then:
1. Inspect git status and git diff.
2. Read the relevant API and shared types.
3. Compare code with the active ADR.
4. Run pnpm -F api test.
5. Report:
   - Facts confirmed by the repository
   - Handoff claims not yet verified
   - Contradictions or missing information
   - Tenant isolation risks
   - The next useful action

Do not modify application code.
Do not mark unfinished work as complete.
Enter fullscreen mode Exit fullscreen mode

These are ordinary commands the agent can use for the repository inspection:

git status
git diff
git diff --cached
git log -5 --oneline

pnpm -F api test
Enter fullscreen mode Exit fullscreen mode

The API test command is not a replacement for testing tenant isolation. The test suite might lack the case where a user has export permission in organization A but lacks it in organization B. That boundary needs an explicit test, along with missing-membership and unauthorized-permission cases. A normal successful export only proves the authorized path that was exercised.

Fresh-session recovery check

The recovery result should separate what was found from what was assumed. A useful report might look like this:

RECOVERED
- ADR-002 is the active authorization decision.
- Shared permission types contain organization scope.
- API middleware includes a membership lookup.

NOT VERIFIED
- Migration behavior with existing memberships.
- Cross-tenant export protection.
- Admin UI permission handling.

CONTRADICTION
- Handoff describes the membership check as added.
- Middleware still has a global-role fallback.

NEXT ACTION
- Review the fallback against ADR-002.
- Add or run cross-tenant authorization tests.
- Do not complete the export feature yet.
Enter fullscreen mode Exit fullscreen mode

That result is much more useful than "I understand the project and can continue." It shows which knowledge survived, where the agent found evidence, and which claims remain unresolved. It also gives you a reviewable boundary before another code change.

For noisy verification work, Claude Code can use subagents to isolate test output, documentation searches, and logs, returning a summary to the main session. Hooks can also filter output, such as retaining only failing test lines. Subagents do not receive the main session's auto memory, so their instructions must include the task references and constraints they need. Any returned summary should preserve failed checks and evidence locations rather than only reporting success or failure.

Avoid turning memory into an archive of everything

A memory system becomes less useful when it accumulates material without a clear purpose. Duplicated architecture rules require synchronized updates. Temporary observations can obscure current decisions. Contradictory instructions force the agent to guess which source has authority.

Claude Code already provides ways to reduce unnecessary context. MCP tool names and server instructions load before use, with additional tool information loaded when needed. Skills load when invoked, rather than requiring every specialized procedure to live in CLAUDE.md. That separation lets you keep stable project guidance short and locate detailed workflows where they belong.

I keep CLAUDE.md under 200 lines, focusing on repository structure, essential commands, security constraints, and pointers to maintained documents. Claude Code also supports a Compact instructions section that specifies what should survive summarization. During compaction, the project-root CLAUDE.md is reread from disk, and invoked skills are reattached with limits of 5,000 tokens per skill and 25,000 tokens total. These mechanisms help preserve selected instructions, but they don't validate whether an architectural claim still matches the repository.

Here is a compact project instruction example:

# Project Instructions

## Repository
- apps/admin: admin application
- apps/api: multi-tenant API
- packages/ui: shared UI components
- packages/shared: shared contracts
- Package manager: pnpm

## Security constraints
- Enforce tenant isolation at the API.
- Resolve organization-scoped permissions through
  the current organization membership.
- Do not treat UI permission checks as authorization.

## Authoritative references
- Architecture decisions:
  docs/architecture/decisions/
- Active task requirements and handoffs:
  docs/tasks/

## Verification
- API test command: pnpm -F api test
- Include cross-tenant cases for authorization changes.
- Report unverified behavior explicitly.

## Compact instructions
Preserve active decision references, changed files,
unfinished work, verification evidence, and risks.
Do not preserve superseded decisions as active rules.
Enter fullscreen mode Exit fullscreen mode

This example intentionally contains no session transcript, detailed migration log, or full architectural history. The architecture records hold the decisions, and the task handoff tracks incomplete work. A specialized skill can hold a repeatable workflow, as discussed in the article on CLAUDE.md and Skills in this series. The purpose of this file is to guide the agent toward the right sources.

The maintenance process is equally important. When a decision changes, update its record, affected project instructions, related tests, and active handoff. Remove obsolete claims from auto memory instead of allowing them to compete with the new design. Discard temporary output when the useful evidence has been captured.

A small context kit you can use

The three templates above form an agent-context-kit/ that you can adapt to an existing repository. They deliberately separate long-lived decisions from task progress and verification. The final file turns that separation into a repeatable recovery procedure.

agent-context-kit/context-recovery-checklist.md

# Context Recovery Checklist

## Identify the working state
- [ ] Confirm the current branch.
- [ ] Inspect uncommitted changes.
- [ ] Check recent commits and base reference.
- [ ] Locate the current task brief.

## Recover decisions
- [ ] Read active decision records.
- [ ] Check superseded decision references.
- [ ] Compare decisions with implementation.
- [ ] Identify conflicting project instructions.

## Recover progress
- [ ] Read the session handoff.
- [ ] Inspect files described as changed.
- [ ] Identify missing or unrelated changes.
- [ ] Separate completed from unfinished work.

## Verify evidence
- [ ] Check which tests actually ran.
- [ ] Run relevant tests against current code.
- [ ] Inspect failures and untested boundaries.
- [ ] Check tenant isolation where applicable.

## Decide what to do next
- [ ] List confirmed facts.
- [ ] List unverified claims.
- [ ] Report contradictions.
- [ ] Propose the next implementation step.
- [ ] Get the change scope reviewed.
Enter fullscreen mode Exit fullscreen mode

For the worked reporting export change, the decision record establishes membership-based authorization, the handoff records the unfinished middleware and migration work, and the recovery checklist forces inspection before implementation. A passing API suite does not close the task until the relevant cross-tenant behavior has been verified. If the middleware still contains a global-role fallback, that contradiction becomes the next item to resolve rather than an assumption silently carried forward.

I use this process to make sessions independently recoverable, not to eliminate useful continuity. Some tasks benefit from keeping the conversation and using /compact; others are easier to restart with a checked handoff. The deciding factor is whether the next session can distinguish established decisions, current repository facts, and unverified claims.

The outcome I'm after is straightforward: an agent should find the right information, recognize when it has become outdated, and continue from evidence rather than recollection. That requires less preserved conversation than many developers expect, but more discipline around decisions, handoffs, and verification.

What information do you wish your coding agent would reliably carry into the next session?

Top comments (0)