Blog/Coding agents
6 min read

Giving Coding Agents Memory That Survives the Session

You spend forty minutes explaining why the payment service can't call the notification service directly. The agent understands, works with the constraint, produces good code.

Then the context window fills. The session compacts, or you close the terminal and start fresh tomorrow. The next session opens with a clean slate, and the first thing it suggests is a direct call from the payment service to the notification service.

This is the defining friction of working with coding agents, and it isn't a capability problem. The model is perfectly able to reason about your architecture. It just has no mechanism for knowing what it concluded yesterday.


What actually gets lost

Not code — that's in git. What evaporates is the layer that never gets written down:

  • Decisions and their reasoning. Not just "we use Postgres" but why DynamoDB was rejected. Without the reasoning, the decision gets re-litigated every few sessions.
  • Conventions. Which error type this codebase uses, how modules are laid out, which patterns were tried and abandoned.
  • Dead ends. The approach that looked obvious and failed for a non-obvious reason. An agent without this memory will propose it again, confidently.
  • Constraints. The service that can't be touched before the migration. The dependency pinned for a reason.

Some of this could live in a CLAUDE.md or an ADR — and for stable, universal facts, it should. But a static file doesn't capture the reasoning from a specific debugging session at 4pm on Thursday, it doesn't accumulate automatically, and nobody maintains it at the granularity where it would actually help.


Compaction is the sharp edge

Long-running agent sessions hit the context limit and compact: the transcript is summarised down to make room.

Summarisation is lossy in a specific and unhelpful way. It preserves the narrative of what happened and drops the details that felt incidental at the time — which is exactly where architectural constraints live. "We discussed the service boundaries" survives. "Payments must not call notifications directly because of the retry storm in March" does not.

So the failure mode isn't just forgetting between sessions. It's forgetting mid-session, in a way that's hard to notice until the agent contradicts something it agreed to an hour earlier.


What persistent memory changes

A memory layer intervenes at both boundaries: it captures durable conclusions as they're formed, and restores them when a new session starts.

phorvec ships this for Claude Code as an explicit feature. One command installs the hooks:

phorvec init --tool claude-code --hooks

That writes the MCP server entry plus three hooks:

  • PreCompact — before the context window compacts, distil the session into a tagged summary and snapshot it.
  • SessionEnd — do the same when the session ends.
  • SessionStart — inject a token-budgeted block of restored context into the new session.

The result is that decisions survive the compaction boundary rather than being summarised away. In a deterministic evaluation seeding a session with decisions and then forcing compaction, 5 out of 5 seeded decisions survived (8 out of 10 at N=10). The baseline without the hooks is 0 — which is the expected result, since nothing was designed to carry them across.

The hooks are hardened to fail silently: any internal error, including a panic, exits 0 with empty stdout. A memory layer that can break your editor session is worse than no memory layer, so the failure mode is "you lose the memory feature for that session," never "your session breaks."


Beyond capture: what the agent can do deliberately

Automatic capture handles the boundaries. The more interesting behaviour is the agent using memory as a tool during a session.

Once phorvec is connected over MCP, the agent can store a conclusion the moment it's reached, recall relevant prior decisions before proposing an approach, and search the memory of past sessions the way it searches the codebase.

Two capabilities matter more than they sound:

Supersession. Facts change, and the store needs a way to record that rather than accumulating both versions with equal standing. phorvec keeps a temporal chain over knowledge-graph entities: fact_supersede replaces an entity with its successor, fact_invalidate marks one no longer valid (fact_reactivate undoes it), and fact_history returns the full chain — so "we moved off Redis" is recorded as a supersession with the old state still auditable, rather than as a second, contradictory present-tense fact.

Retrieval that handles identifiers. Coding memory is full of symbol names, error codes and file paths — exactly what pure semantic search is worst at. phorvec fuses vector search with a BM25 keyword index so UserServiceImpl matches the memory that names it rather than every service-shaped class nearby in embedding space. The mechanics are here.


Code-aware indexing

Beyond conversational memory, phorvec can index the codebase itself into the same searchable store, with chunking that follows language structure rather than byte offsets:

LanguageChunk boundaries
RustFunctions, impl blocks, modules
PythonFunctions, classes, modules
TypeScript / JavaScriptFunctions, classes, top-level statements
GoFunctions, types, packages
JavaMethods, classes
MarkdownHeadings and sections

Chunking on structure means a retrieved chunk is a whole function rather than its last nine lines plus the start of the next one. rag_index_directory walks a tree respecting .gitignore and skipping binaries; rag_refresh re-indexes only what changed.


When the team is the unit

The problem compounds across people. Every engineer's agent re-derives the same architecture from scratch, and two agents can reach contradictory conclusions without either knowing the other exists.

Shared team memory addresses this: a pool every engineer's agent can read, governed by sharing modes — FULL for tightly coordinated work, PROTECTED for read-with-approval-to-write, ISOLATED when agents publish discrete artifacts without exposing working memory.

Contradictions get caught rather than silently served. conflict_check scans a shared pool for statements that semantically oppose each other, classified by severity, using negation detection over semantically similar items. There's a two-agent walkthrough in the docs showing one contradiction surfacing without either agent reading the other's memory.

Setup is one line per engineer against a self-hosted instance — see Team Hosting.


Setting it up

phorvec speaks the Model Context Protocol, so any MCP client can use it. The binary writes the config itself:

phorvec init --tool claude-code

Supported targets include claude-code, cursor, windsurf, zed, cline, copilot, aider, antigravity and codex. Or add it by hand:

{
  "mcpServers": {
    "phorvec": {
      "command": "phorvec",
      "args": []
    }
  }
}

New installs default to a minimal 18-tool preset so the agent isn't handed a hundred tools it will never call; --preset standard|full opens that up.

Everything runs locally. Agent databases live in ~/.phorvec/data/ by default, the embedding model is bundled in the binary, and nothing leaves your machine — which matters when the memory in question is your architecture. See The case for one file per agent.

The Community tier is free and needs no key.

pip install phorvec

Full setup instructions per client are in MCP Client Setup.

phorvec is a local-first memory layer for AI agents — one portable file per agent, MCP-native. Read the docs or download the free Community tier.