Skip to content
Vibedata

Practice

CONTEXT.md and ADRs as the agent's durable memory

Two plain files in the repo, a glossary and numbered decision records, give a data agent memory that outlasts the session. The method is a ranking and a write rule, and you can adopt it without us.

A balance scale tips to the left. Its low pan holds a stack of pages: CONTEXT.md on top of decision records ADR 0001 and ADR 0002. Its high pan holds a faded, dashed cloud labelled Session memory.
The record the team already keeps outranks the memory an agent infers.

By Vibedataagent-memorycontext-mdarchitecture-decision-records

Our data engineering plugin asks a domain repo for two plain files at its root: a CONTEXT.md that defines the domain's words, and numbered decision records in docs/adr/ that say what the team decided and why. When the agent finds a conflict between those files and the memory it has built up across sessions, it follows the files.

A lot of writing about agent memory is about storage and retrieval: vector stores, embeddings, summaries kept in the context window. The more useful question is which record wins when two of them disagree. That question has a plain answer in a repo that already keeps decisions the way engineering teams have kept them for years.

The ranking

The agent's instructions set a fixed order for conflicts:

  1. The design record for the work in progress. It is the most specific record, and it can supersede an older decision, but it has to say so.
  2. The glossary and the accepted decision records.
  3. Domain memory: the softer notes the agent accumulates over sessions.

When the third disagrees with the second, the agent follows the documented record and flags the conflict. Memory still does its job where the files are silent, such as a preference nobody wrote down or a working note from last week. It does not get to overrule a file.

We chose this order because of how each source drifts. A stated convention and an accepted decision stay put until someone changes them on purpose. A summary of last month's conversations drifts with every pass, because the summariser has to guess which decision was final and which was a passing remark in a thread. A decision record removes the guessing. It says what was decided, which options were considered, and what later work has to respect.

The ranking is the part a reader can copy. The storage is a text file in git.

The write rule

The ranking only works if the records are right, and that depends on who writes them and when. The answer we settled on is that the agent writes to the record as part of the work, in the same change that does the work, and never as a separate task at the end.

The record changes at four points in the work:

  • A new term comes up while the team is framing the work. It goes into CONTEXT.md at that point, not at the end.
  • A design decision is hard to reverse, surprising without context, and the result of a real trade-off. It becomes a decision record.
  • Review changes a decision after it was drafted. The record is updated to match the decision that was actually made.
  • Before the change merges, a final sweep catches anything the earlier points missed.

A fifth point stops work rather than writing to the record. A fix that would contradict an accepted decision halts and recommends a build. It does not quietly patch around the decision.

Every one of these lands in the pull request that carries the work. There is no separate approval step for the record. It is reviewed the way any other change to the repo is reviewed.

That last point matters more than it looks. A memory the team can't review is a memory the team can't trust. Keeping it in the same diff as the change that caused it means the reviewer sees the decision and the code it governs together.

Reversing a decision is a product change

An accepted decision is never rewritten in place. If it stops holding, a new record supersedes it, and the old one is marked as rejected with a pointer to its replacement. The history stays readable. Someone looking at the repo later can see that a decision was reversed, when, and what replaced it.

The agent's instructions say the same thing from the other side: reversing a decided record is always a product change, not something an agent decides on its own in the middle of a task. The rule sits in the instructions the agent reads every time, not in a document it might skip.

No example values

These files are committed to the repo and read straight into every agent's context. Anything written in them goes wherever that context goes, session after session. So the discipline has to live in the writing step.

A decision record states the structure of the decision. It can say that a staging grain is order line rather than order, or that nulls drop for a set of accounts carried over from a legacy system. It does not carry the account identifier a user mentioned in passing, a real email, or a row the agent just read while it was gathering the requirement. If a user illustrates a quirk with a real value, the agent generalizes it before anything is written down.

This is the rule we would copy first. A glossary full of real identifiers is a leak that nobody notices until someone reads it aloud.

What a team can do on Monday

The method doesn't need our product. A team that wants the same memory can start with three things.

First, a glossary at the root of the repo. Add a term when it resolves, never speculatively. Every term should be used by something: a model, a decision, or a design record.

Second, a decision record folder with a short rule for when a decision earns a record. We use three tests: hard to reverse, surprising without context, and the result of a real trade-off between named options. A choice that is easy to change later belongs in the design of the thing it affects, not in the decision log.

Third, one line in the repo's agent instructions that states the ranking: the current design, then the glossary and the records, then anything the agent remembers. Every agent that opens the repo then reads the same rule, and a conflict has a known answer before anyone argues about it.

After that, the write rule does the rest. Put the record in the same pull request as the work, and make the reviewer see it there.

Where it came from

We run the same pattern on our own engineering repositories. The product's agents read a repo's decision records before they plan, and this plugin repo relies on the same kind of files. We wrote the habit for ourselves first and then pointed it at the customer's domain repo.

What we have not measured

We have not measured how often the ranking changes an agent's plan, or how often a record catches a conflict that memory would have missed. The claim in this post is about the design: a plain text record, a fixed order, a write rule, and no real values in the files. It is not a benchmark, and we will say so if we publish one.

The part we are most confident in is the write rule. A record that is written in the same change as the work has a reviewer looking at it. A record written later, or by no one, is the one that goes stale.

So the question to take back to your repo is a practical one. Which decision would your agent get wrong today, because the answer lives in a chat thread from March and not in a file it reads first?

Get the weekly roundup by email

One email a week: what got easier in Vibedata, a recipe to try yourself, and where data engineering is heading.