The ADR model assumes a stable answer
An architecture decision record captures one decision, its context, and its consequences. The classic lifecycle is short: a decision is proposed, accepted, and eventually superseded by a later one. That model works well for a system with one architecture at a time, which is the situation most ADR practice was developed for.
A migration violates the assumption. For years the honest answer to "how does this system handle persistence" is two answers, both correct, depending on which component you are looking at and how far the migration has reached. An ADR corpus that can only say accepted or superseded cannot express that, so teams either write rules too broad to be enforceable or leave the migration out of the corpus entirely.
If you have not read it, how AI coding agents use ADRs covers the general case: whether records are ignored, advisory, or enforced, and what it takes to make them executable. This piece is about what changes when the same corpus has to describe a system in transition.
What a migration adds to the lifecycle
Three things are true during a migration that are not true the rest of the time, and each needs a representation.
- Two answers are simultaneously valid. The legacy pattern is correct in components that have not migrated. The target pattern is correct in those that have.
- Some exceptions are deliberate and temporary. Adapters, dual writes, and compatibility layers are not violations. They are planned, and they are supposed to disappear.
- Correctness is positional. Whether a given call is acceptable depends on which tree the file sits in, not on the call itself.
States a migration ADR needs
Four states are enough. They are not exotic additions to ADR practice so much as the states a migration was always in, written down.
| State | What it asserts | Lifecycle |
|---|---|---|
| Legacy | This was the standard. It remains in force where migration has not reached. | Retired when the last component migrates. |
| Target | This is what new and migrated code follows. | Becomes the only active decision at completion. |
| Transitional | This exception is permitted at a declared boundary during cutover. | States its termination condition in prose. Marked status: superseded when the boundary closes. |
| Forbidden reintroduction | This pattern must not return to a component that has migrated. | Stays status: accepted with selectors covering the migrated tree. It is the record that prevents the pattern coming back. |
The last row is the one teams skip and the one that matters most after the migration is nominally finished. It is the only thing standing between a migrated module and the next agent that has no local reason to avoid the old approach.
Mind the vocabulary here, because two meanings of the same word sit next to each other. The pattern is superseded, and the decision that forbids it is status: accepted, scoped to the migrated tree. An ADR whose own status: is superseded is excluded from the active set entirely, which is how you retire a record rather than how you enforce one. Marking the forbidding decision superseded would switch it off.
Applicability is what makes the states work
States alone do not resolve anything. A decision that says "direct persistence access is superseded" is either wrong in the legacy tree or ignored everywhere, unless it also says where it applies.
Mneme compiles the ## Constraints block of an ADR into typed rules, and those rules take explicit path selectors. include_paths and exclude_paths determine applicability only; they never loosen the matching semantics. A rule can govern the migrated tree while staying silent where migration has not reached.
The state says what the decision asserts. The path selectors say where the assertion is in force. A migration needs both.
The practical effect is that a check reports three outcomes rather than two. A rule can pass, fail, or be skipped because it did not apply to the path in question, and the skip is reported with its reason. During a migration the skip is the informative case, because it is the machine confirming that this component has not moved yet.
Writing the decision down
A migration ADR looks like an ordinary one. The frontmatter carries the identity and status, the prose carries the reasoning a future engineer will need, and the constraints block carries the part a machine evaluates.
The reasoning section matters more here than in a typical ADR, because a migration decision is read most often by whoever is deciding whether an exception still applies. Recording why the adapter exists and what has to be true before it can be removed is what stops the exception from silently becoming the architecture.
Sequencing belongs in the corpus
Migrations carry ordering requirements. A downstream component cannot move until its dependency has. Those requirements normally live in a project plan, which is not something an agent is checked against.
No directive expresses "has component A migrated yet." What the corpus can carry is a rule that holds the downstream tree to the legacy pattern until its dependency lands, retired as part of that dependency's migration. That keeps the ordering visible and attributable rather than machine-derived, and it gives the team one place to answer the question that comes up constantly during a long migration, which is whether a particular piece of work is allowed to start yet.
Retiring decisions is part of the work
The end state of a well-run migration corpus is small. The transitional decisions have been marked superseded as their boundaries closed. The legacy decision has been retired. What remains is the target decision plus the superseded records that keep the old patterns from returning.
Reaching that state requires treating retirement as scheduled work rather than cleanup that happens if anyone remembers. A transitional decision without a termination condition is how a temporary adapter becomes a permanent layer that nobody can justify and nobody dares remove.
Conclusion
Most ADR advice assumes the system has one architecture and the record's job is to say what it is. A migration needs the corpus to describe a system with two, to say which one governs which paths, and to mark the exceptions that are supposed to expire.
That is a modest extension to ADR practice and a significant change in what the corpus can do. It turns the migration plan from a document agents do not read into a set of constraints they are checked against.
The ADR compiler demo shows a record compiling into the typed rules an enforcement layer evaluates.