Prevent architectural drift from AI coding agents with a closed control loop: record current architecture decisions with explicit scope, route the applicable records before planning, require stable decision citations, route the final diff again, and enforce objective consequences with deterministic tests or policy checks. Keep qualitative decisions under named human review, and record every approved exception with narrow scope and an expiry.
Follow the complete control loop.
This page is part of a connected guide to recording, routing and enforcing decisions in coding-agent work.
What is architectural drift from AI coding agents?
Architectural drift is the gradual divergence between a system's intended architecture and the code that is actually merged. With AI coding agents, each change can be locally plausible, pass its feature tests and still weaken a boundary that the team expects to remain intact. One task adds a second persistence pattern. Another crosses a package boundary. A later task copies that new pattern. No single patch looks catastrophic, but the system moves away from its accepted design.
The problem is not that an agent deliberately ignores architecture. The agent can only use the authority that reaches its working context and the evidence available in the repository. Source code shows what exists. It rarely proves why a pattern exists, whether an alternative was rejected, or which rule is still current. If the governing decision is absent or ambiguous, the agent must infer intent from incomplete evidence.
Preventing drift therefore requires more than a larger prompt. A team needs a way to preserve architectural intent, match it to the work, and verify the result. The practical control loop is record, route, cite, enforce and review.
The short answer: use seven controls
Use seven connected controls. Each control prevents a different failure. Removing one leaves a gap that another control cannot cover completely.
- Record: preserve important decisions, rationale, rejected alternatives, scope and lifecycle beside the code.
- Resolve: determine which current records govern the task's repositories, paths and capabilities.
- Route: give the agent a compact, cited brief before it designs the change.
- Cite: require the plan and pull request to name the decisions that shaped the implementation.
- Re-route: resolve the actual final diff because implementation often crosses boundaries that planning did not predict.
- Enforce: connect objective consequences to deterministic architecture tests, dependency rules or CI checks.
- Review: keep conflicts, exceptions and qualitative consequences under explicit human authority.
1. Define the architecture that must not drift
A team cannot detect drift from an architecture that exists only in memory. Start with the boundaries that reviewers repeatedly defend: approved persistence choices, dependency direction, service ownership, API compatibility, data retention, security controls and migration rules. Record the reason as well as the rule. The reason helps an agent apply the decision when the new task is not identical to the example that caused it.
Do not attempt to document every convention at once. Choose decisions whose loss creates expensive rework, operational risk or repeated review comments. Give each record a stable ID, owner and lifecycle state. An accepted record can govern work. A proposed record can support discussion. A superseded record remains useful history but must not be returned as current authority.
Rejected alternatives are part of the architecture. If the team evaluated direct database access from HTTP handlers and rejected it, record that result. Otherwise, a later agent can reintroduce the same approach because it looks simple in the local task.
# DEC-021: Domain code does not import infrastructure
## Status
Accepted
## Context
Domain behaviour must remain testable without database, network or
framework adapters.
## Decision
Code under src/domain/** must not import from src/infrastructure/**.
## Applies To
- src/domain/**
## Consequences
Adapters may depend on domain interfaces. Domain code must not depend
on adapter implementations.
## Revisit When
- the package boundary or ownership model changes2. Give every decision explicit scope
A useful record says where it applies. Repository-relative paths and documented globs provide an inspectable starting point. Components, capabilities and interfaces can add cross-cutting scope when one decision governs several directories. The scope should be narrow enough to avoid loading unrelated records and broad enough to cover the real boundary.
Do not use the repository root as the scope for every decision. That turns routing into a full-document dump and makes one relevant rule compete with many unrelated rules. It also hides mistakes: a desktop storage decision should not govern a hosted database merely because both exist in the same repository.
Validate literal paths and test representative matches. A scope entry that points to a renamed directory creates false confidence. A small fixture set can establish that the expected paths match, unrelated paths do not match and superseded records are excluded.
3. Route decisions before the agent plans
Ask the agent to identify the likely change surface before it proposes an implementation. Resolve the intended repositories, files and capabilities against the current decisions. Then deliver only the matching records in a focused brief. Include the decision ID, status, source, matching scope, operative constraint and any boundary or exception that matters to the task.
Path routing establishes declared applicability. Topic search can help when the exact files are not known, but semantic similarity should not silently become authority. A record can sound relevant and still be out of scope. Conversely, a path-scoped decision can govern the work even when the task description uses different words.
The agent should stop when live decisions conflict, when a required source cannot be read or when the task appears to require a violation. It should not invent precedence from timestamps, filenames or search scores. The owners must clarify scope, supersede a record or approve a bounded exception.
# Resolve the current decisions for an intended path.
decided decisions-for src/domain/orders/price.ts decisions/ --json
# The brief should retain the decision ID, source and match reason.
# An empty result is evidence of an ungoverned path, not permission to
# claim that the change complies with project architecture.4. Make decision citations part of the change
A citation shows which authority the agent used. Require the implementation plan to name the governing IDs beside the affected design choices. Preserve those IDs in the pull request with the repository revision used for routing. This makes the route inspectable and helps a reviewer distinguish a missing record from an implementation that did not follow a record it received.
A citation is evidence of retrieval, not evidence of compliance. An agent can cite DEC-021 and still introduce the forbidden dependency. Keep the citation because it improves provenance, then use an objective check or human review to evaluate the result.
Do not retain hidden reasoning traces as the decision record. The durable evidence is smaller: task, repository revision, applicable IDs, source paths, changed paths, checks, approvals and any exception. That evidence can be reviewed without treating a generated explanation as project authority.
5. Route the final diff again
The initial plan is a hypothesis. The final diff is the observed change surface. An agent may add a migration, edit a shared package, change a deployment workflow or update an API contract that was not listed in the plan. Re-run the resolver against every changed path before review so each new boundary brings its decisions into the pull request.
Compare the final result with the planning brief. A new decision may require a test or owner review. A decision that disappeared may be harmless because its expected file was not changed, but the difference should be visible. If routing runs only at session start, the workflow can miss the exact scope expansion that causes drift.
This second pass also creates useful operational data. Repeated unplanned matches can show that task planning is weak, a shared package has too much responsibility, or decision scope needs refinement.
git diff --name-only origin/main...HEAD > changed-paths.txt
decided herald decisions/ --paths-file changed-paths.txt --link-base https://github.com/owner/repo/blob/HEAD --out governing-decisions.md6. Enforce objective consequences in CI
Turn a decision into an automated gate only when the repository contains objective evidence. Dependency direction, forbidden imports, required files, schema compatibility, approved packages and migration properties are good candidates. An architecture-test library, linter, policy engine or ordinary test can provide the detector. The technology matters less than the truthfulness of the claim.
Each failure should name the governing decision, exact scope, detected fact and remediation. This connects the red build to architectural intent. It also prevents an old check from becoming detached policy after the decision is superseded.
Choose the check mode explicitly. A changed-files check can establish that the pull request did not introduce a new violation in its evaluated paths. A full-tree check can make a wider claim. Do not present incremental coverage as proof that the whole repository complies.
# Validate the decision corpus and its relationships.
decided gate decisions/
# Include explicitly classified code constraints for this diff.
decided gate decisions/ --code --base origin/main
# Run the code-enforcement surface directly when required.
decided sentry decisions/ --base origin/main7. Keep judgement and exceptions explicit
Not every architectural consequence is machine-checkable. Whether a service boundary is worth its operational cost, whether an interface is understandable or whether a migration risk is acceptable requires judgement. Route the decision to the named reviewer and label the result as human approval. Do not hide the limitation behind an automated score.
When delivery requires a temporary deviation, create an exception rather than weakening the default rule. Name the governing decision, exact scope, reason, owner, approver, compensating control and expiry. Retrieval should show the decision and the matching exception together. CI should apply the exception only to its declared boundary.
Review exception patterns. Frequent exceptions can mean that the architecture has changed, the decision is too broad or the detector measures the wrong fact. The right response may be a superseding decision, not a permanent collection of suppressions.
Which control prevents which failure?
The controls work together, but they are not interchangeable. Use this mapping when an existing workflow has a recurring failure.
- The agent repeats a rejected approach: record the alternative and make it discoverable with the accepted decision.
- Every task receives a large ADR dump: add explicit scope and route only current matches.
- The right record exists but the agent never sees it: run routing before planning and preserve the brief.
- The agent touches an unplanned subsystem: route the final diff and compare it with the initial result.
- Reviewers repeat the same import or dependency comment: link the decision to a deterministic architecture check.
- Two accepted records prescribe incompatible outcomes: stop and resolve the conflict through ownership or supersession.
- A temporary workaround becomes permanent: record a narrow exception with an owner and expiry.
- A green check overstates compliance: report the detector, evaluated scope and evidence rather than a generic governance pass.
Worked example: stop a second persistence pattern
Assume a coding agent is asked to add delivery preferences to a desktop application. The repository already uses SQLite for local workspace state, but the task does not mention persistence. Without a governing record, the agent might add a small JSON store because it looks sufficient for one setting. The feature works, yet the application now has two persistence patterns, two migration paths and an unclear source of truth.
The team records DEC-014 for desktop persistence and scopes it to apps/desktop/** and packages/local-store/**. The planning route matches the intended settings path and returns DEC-014. The brief says that local persistent state remains in SQLite and that hosted persistence is outside the decision. The agent cites DEC-014 in its plan and adds the new setting to the existing store.
The final diff includes a new migration file, so routing also returns the reversible-migrations decision. CI runs the migration tests and a dependency check that rejects additional database clients in the desktop package. A reviewer still evaluates the user-facing default because the preferred behaviour is a product judgement. The workflow prevents architectural drift without asking one instruction file or one automated check to do every job.
A pull-request checklist for architectural drift
Use the same questions for human and agent-authored changes. Architectural authority should depend on the repository and the decision owners, not on who produced the patch.
- Which accepted decisions govern the intended paths and capabilities?
- Does the plan cite those decisions beside the affected design choices?
- Did the final diff cross any new decision scope?
- Are superseded or proposed records excluded from governing results?
- Does each automated check name the decision and the bounded fact it evaluates?
- Are judgement-based consequences assigned to a named reviewer?
- Does every deviation have a scoped, approved and expiring exception?
- Can a later reviewer reconstruct the route, checks and approvals from durable evidence?
Adopt the control loop without stopping delivery
Start with one boundary that repeatedly causes review work. Record the decision, add precise scope and test retrieval against a few representative paths. Run routing in advisory mode on real tasks and inspect false matches, missing records and conflicts. Tighten the data before making any result a merge requirement.
Next, connect one objective consequence to an existing test or policy check. Make the output cite the decision. Add final-diff routing and an exception template. This creates one complete control loop that the team can observe and improve.
Expand by cost and reuse. Add decisions that affect many tasks or prevent high-impact mistakes. Measure expected-decision recall, irrelevant matches, citation completeness, final-diff coverage, check execution and repeated reviewer corrections. The goal is not the largest decision catalogue. It is fewer changes that unknowingly contradict current architecture.
- Week 1: record one costly boundary with owner, status, scope and rejected alternatives.
- Week 2: route it before planning on real tasks and test expected matches and exclusions.
- Week 3: add one deterministic check linked to an objective consequence.
- Week 4: route final diffs, preserve citations and introduce expiring exceptions.
How AsDecided supports architectural drift prevention
AsDecided stores typed, human-readable decision records in Git and validates their lifecycle and relationships. Core provides deterministic CLI and read-only MCP retrieval so people and coding agents can inspect the same source without giving the retrieval layer permission to rewrite it.
Path-scoped lookup resolves current decisions for repository files. Decision-aware pull-request output reports why records apply to the final diff. The gate validates the decision corpus and can include explicitly classified code constraints, while Sentry evaluates those constraints without an LLM judge. Stable IDs and source locations preserve citations throughout the change.
These tools support the control loop; they do not replace architecture ownership. Teams still decide which records are accepted, which consequences are objective, who reviews qualitative trade-offs and when an exception is justified. The specification, current product documentation, source and changelog define the released behaviour.
Conclusion
AI coding agents can increase the rate of change. They also increase the value of making architectural intent explicit. The durable answer is not to make every prompt longer. It is to preserve current decisions, route the relevant authority into each task, cite it in the change, inspect the final diff and automate only the consequences that have objective evidence.
That closed loop makes drift visible before it compounds. When a rule is missing, the team can record it. When routing is wrong, the scope can be corrected. When a patch violates an objective boundary, CI can show the exact decision and evidence. When judgement is required, a named person remains responsible. The architecture can then evolve through explicit decisions instead of a sequence of plausible accidents.
Sources and further reading
- AsDecided product overview ↗
- AsDecided decision scope and relationships ↗
- AsDecided enforcement policy and gate ↗
- AsDecided decision-aware pull requests ↗
- AsDecided v0.28.0 explainable retrieval release ↗
- AWS guidance on architectural decision records ↗
- OpenAI AGENTS.md documentation ↗
- GitHub repository custom instructions ↗
- ArchUnit architecture tests ↗
Product claims should be checked against the AsDecided canonical source map.