Mneme HQ governs AI-assisted software migrations by separating legacy, transitional, and target architecture into decisions that coding agents are checked against before they generate code. During a migration a repository contains more examples of the architecture you are leaving than the one you are moving toward, so an agent that infers patterns from the codebase infers the wrong one. Path applicability decides which decision governs which tree. Retrieval tells an agent what exists. Governance tells it what should survive.
Reference Architecture · Simulated Scenario

Keeping AI Coding Agents Inside the Target Architecture During Migrations

A migration leaves legacy and target patterns in the same repository. Mneme HQ scopes each architectural decision to the paths it governs, so agents build toward the architecture you are migrating to.

MIGRATION GOVERNANCE PIPELINE mixed repository Legacy + Target both patterns in one tree migration ADRs Decision Corpus legacy · transitional · target include / exclude_paths Scope Resolution which rule governs this path before generation Enforcement target architecture holds
The Problem

During a migration, the codebase teaches the wrong architecture.

Suppose the system you are leaving is Controller → Service → Repository → Database and the system you are moving toward is API → Domain → Port → Adapter. While the migration runs, both structures legitimately exist in the repository. That is not a defect. It is what a migration is.

Now ask a coding agent to add a feature. It inspects the codebase, finds two hundred examples of the old structure and twenty of the new one, and concludes that the old structure is how this system is built. Statistically it is correct. Architecturally it has just reproduced the thing you are paying to remove.

The dominant pattern in a repository may be precisely the pattern you are trying to eliminate. What exists in the codebase is not what should be created next, and frequency is not intent.

Without Mneme HQ — agent follows the majority:
Prompt: "Add customer lookup to the migrated orders service"
I'll follow the existing pattern in this codebase and query the database directly from the service layer...

db.query(Customer).filter_by(id=customer_id)
With Mneme HQ — applicable decision resolved:
mneme check --mode strict
FAIL [ADR-042] direct database access is superseded
Path: modern/orders/customer_lookup.py
Applies: migrated modules only. The legacy tree is excluded.
Use: CustomerRepository via RepositoryPort.

→ The pattern the agent copied is the one being removed.
Why Existing Tools Fall Short

Nothing in the toolchain knows which architecture is the target.

Approach Limitation With Mneme HQ
Codebase context and retrieval Reports what the repository contains, and during a migration legacy is most of it Decisions state which pattern survives, independent of how often it appears
Migration plan in the prompt Advisory, and silently dropped when context is compacted mid-session Compiled decisions are evaluated before generation in every session
Linters and static analysis Apply uniformly; cannot permit a pattern in legacy code and forbid it in migrated code Path applicability scopes each rule to the tree it actually governs
Code review Sees the diff after the wrong pattern was already copied, at AI output volume Enforcement runs before generation and again as a deterministic CI gate
Deleting the legacy code Unavailable until the migration finishes, which is the entire problem Both architectures coexist under explicit, separately scoped decisions
How Mneme HQ Solves It

Give every migration state its own decision.

A migration has more than two states. Treating it as "old code" and "new code" is what makes agents guess. Four states are enough to describe almost any migration, and each one maps onto a decision the enforcement layer can resolve.

1

Record the four migration states

Legacy was valid historically and is no longer the model for new work. Target is what new and migrated code follows. Transitional covers the adapters, dual writes, and compatibility layers that exist only during the cutover. Superseded is the pattern that must not return to a component once it has migrated.

2

Scope each rule to the paths it governs

include_paths and exclude_paths decide where a rule applies. A target rule governs modern/** and leaves legacy/** alone, which is what lets both trees coexist without the agent inferring which one is canonical.

3

Compile with mneme adr import

The Constraints block of each ADR compiles into typed rules the enforcement layer evaluates. Applicability is resolved against the real target path at check time, and every check reports which rules applied, which were skipped, and why.

4

Gate generation and CI on the applicable decision

Run mneme check --mode strict before the agent writes, and again in CI. A migrated module that reaches for the superseded pattern fails against the decision that governs its path, no matter which agent produced the change.

Technical Implementation

What a scoped migration decision looks like.

The decision below supersedes direct database access, but only inside the migrated tree. The legacy tree keeps its existing calls until its components are migrated, so the rule does not fire on code that has not moved yet.

docs/adr/ADR-042-repository-port.md
---
id: ADR-042
title: Direct database access is superseded by RepositoryPort
status: accepted
priority: foundational
date: "2026-08-28"
scope: code
---

Direct database access was the standard in the pre-migration architecture.
Migrated modules reach persistence through RepositoryPort. The legacy tree
keeps its existing calls until its components are migrated.

## Constraints

- FORBID_LITERAL:
    value: db.query(
    include_paths:
      - "modern/**"
    exclude_paths:
      - "legacy/**"
Terminal — enforcement with applicability resolved
$ mneme adr import docs/adr/
Compiled 3 migration decisions.

$ mneme check --mode strict

PASS [ADR-041] migrated module resolves persistence through RepositoryPort
SKIP [ADR-042] legacy/orders/service.py
  Reason: excluded by exclude_paths. This component has not migrated.
FAIL [ADR-042] FORBID_LITERAL "db.query("
  Path: modern/orders/customer_lookup.py
  Reason: direct database access is superseded in migrated modules.

Result: FAIL (1 violation, strict mode)

The SKIP line is the part that matters during a migration. The same rule that fails in the migrated tree is deliberately silent in the legacy tree, and the check reports why. An agent working across a half-migrated repository gets a different answer per path instead of one blanket rule it has to reason around.

The enforcement path itself is shown end to end in the repository pattern demo, where a direct SQL call from a service layer is matched to its decision and denied by a strict check.

Simulated Outcome

What changes once migration states are enforced.

4
migration states encoded per boundary: legacy, target, transitional, superseded
10:1
legacy-to-target example ratio the agent sees in the reference repository
0
superseded-pattern violations reaching review from migrated modules
⚠ These figures describe a simulated reference scenario, not live customer data.
FAQ

Common questions.

How is this different from putting the migration plan in the prompt?
A prompt is advisory and is dropped when context is compacted. Compiled decisions are queried before generation in every session, so the constraint survives a migration that runs across many sessions, many agents, and many months.
The legacy pattern is still valid in unmigrated modules. Does Mneme forbid it everywhere?
No. Applicability is scoped by path. A rule can carry include_paths for the migrated tree and exclude_paths for the legacy tree, which is what lets both architectures coexist without the agent guessing which one governs the file it is editing.
What happens to a decision when a component finishes migrating?
Its status changes. A transitional decision that permitted a compatibility adapter is marked superseded once the adapter is removed, and the target decision governs that path from then on. The superseded record stays as the reason the exception existed.
Do we have to model the whole migration before this is useful?
No. Start with the boundary agents cross most often, usually persistence or service-to-service calls. Two or three scoped decisions cover most incorrect pattern copying, and further boundaries are added as the migration proceeds.