Est.

Encoding Architectural Constraints as Machine-Readable Rules

Explicit architectural rules let AI agents detect design violations before they accumulate.

· 9 min read
Cover illustration for “Encoding Architectural Constraints as Machine-Readable Rules”
Architecture Integrity · October 21, 2026 · 9 min read · 1,949 words

Architecture drift in AI-generated codebases occurs when code satisfies a task but violates the system’s intended boundaries. Encoding architectural constraints as machine-readable rules makes those violations detectable before they accumulate. Encoding these constraints requires making tacit design knowledge explicit, versioning it with the code, and enforcing it inside both the agent’s feedback loop and CI.

Why AI-generated code diverges from your intended architecture even when it works

A controller reaches directly into the database layer, and the code works. The query returns the expected data, but the implementation bypasses the service boundary that governs the system. The agent has fulfilled the local task without preserving the larger design. This is a structural engineering problem. The task was visible, while the architectural prohibition was not.

The same mismatch allows event-only communication between modules to become a direct import dependency, or a circular dependency to form between auth and notification services. Each change can be syntactically valid while contradicting the intended architecture. Agents depend on training patterns and the context they receive to understand the system’s design rationale. A large codebase may not even fit within that context, so reading existing code cannot reliably fill the gap.

Architecture decay predates agents, but generation changes its speed and scale. Shortcuts that once accumulated over months can arrive in a single prompt touching dozens of files. Graphide makes the missing design model visible by rendering the repository as a live, navigable architecture graph. That provides structural context against which an agent can be steered, although visible structure still needs explicit rules to distinguish permitted dependencies from prohibited ones.

Where the real design knowledge actually lives — and why it is hard to extract

The absence of a dependency does not explain why it is absent. A layer may contain no database calls because engineers deliberately prohibited them, or because no task has yet required one. Existing code records the outcome, but not necessarily the decision. Engineers learn the difference by talking with others, looking at past reviews, and spending weeks reading the codebase. An agent receives a prompt and a context window instead.

Some of the hardest knowledge to extract concerns dependencies that must never exist. Other decisions depend on domain or organizational context, such as whether a service handles regulated data. Still others keep the design open to future changes that the team expects. That anticipated change may leave no visible trace in the current implementation. The business rationale, rather than the code alone, explains why a boundary matters.

This makes better prompting an incomplete answer. A prompt can convey a known decision, but it cannot recover a rationale that the team has never articulated. Nor can every rationale become a deterministic check, because some decisions still need people to judge the context. Making those judgments into fixed rules risks over-enforcement, while offering checkable prohibitions as advice allows under-enforcement. If tacit knowledge cannot be externalized, agents will keep violating it silently.

A working taxonomy of architectural constraints that can actually be made explicit

Separating structural prohibitions from judgments about when they apply provides a practical starting point. Layer and dependency direction rules are structurally deterministic. A rule can prohibit imports from the db package into controllers, or prevent modules from depending on a higher abstraction level. Module isolation rules similarly make loose coupling checkable by prohibiting imports between users and billing while permitting explicit contracts through events or DTOs.

Circular-dependency guards check whether a dependency path leads back to the module where it started. Pattern-enforcement rules require code to follow a set route, such as passing every user-facing endpoint through AuthorizationMiddleware before it reaches its handler. Naming and symbol rules say, for example, that domain events must end with 'Event' and queries must implement IQuery. These categories provide a practical, non-exhaustive framework. The structural condition can be automated once its scope and meaning are explicit.

Marri’s Constitutional Spec-Driven Development, introduced in January 2026, describes a versioned, machine-readable Constitution derived from CWE/MITRE vulnerabilities and regulatory frameworks. It includes MUST/SHOULD/MAY enforcement levels and CWE traceability, demonstrated on a banking microservices system. Human judgment still determines which obligations apply. Relevance is therefore separate from enforceability, and a constraint that depends on unencoded organizational or deployment context cannot become a reliable CI check merely by being important.

How Architecture Decision Records become enforceable rather than just readable

An Architecture Decision Record becomes a living artefact when its decision participates in the engineering workflow. A static ADR explains why a boundary is there, but the code can cross it without anything breaking. ADR-as-Code connects that explanation to decisions versioned in Git, reviewed through pull requests, and validated in CI/CD.

A practical schema includes a stable ID, status, scope, requires/prohibits, and an enforcement reference. The stable ID names the decision, and scope says where it applies. The requires/prohibits fields state the obligation explicitly. The enforcement reference names the engine, such as ArchUnit, dependency-cruiser, or import-linter, together with the applicable scope and what failure means. These fields connect the reason for a rule to the mechanism that checks it.

A corpus containing deprecated and superseded ADRs can supply an obsolete decision as though it were current. With machine-readable status, agents can tell which policy is current and which rationale belongs to the past. Even a well-structured ADR, however, is only enforceable if its enforcement reference points to a working fitness function.

Fitness functions as the enforcement mechanism that makes rules computational

A fitness function turns a declared constraint into a computational check of what was actually built. When code crosses the boundary, the check fails the build. Unit tests guard business logic in the same way that fitness functions guard architectural integrity. Neither form of protection exists merely because the intended behavior has been documented.

Shumilov’s AI-Readable Architecture (2026) describes a controlled experiment in which Claude generated code for five tasks. A vanilla Express setup had a high rate of architectural violations, while SysMARA v0.7.1 had zero violations. SysMARA makes structural constraints first-class, formally defined, machine-traversable artifacts through an AI System Graph with properties of Determinism, Impact Completeness, and Constraint Visibility. The result demonstrates the importance of explicit structural constraints in that experiment, rather than establishing a universal guarantee for generated code.

The tooling spans language stacks. ArchUnit checks architecture in its language stack, and another architectural testing tool does so for.NET. JavaScript and TypeScript teams have dependency-cruiser and eslint-plugin-boundaries, and teams using another language have import-linter. ArchUnit 1.3’s FreezingArchRule makes legacy adoption practical by recording existing violations in a store on the first run and failing on new violations thereafter. Existing debt is frozen rather than excused, and the baseline can shrink as violations are removed.

Shopify’s modular monolith illustrates this approach in production. Each pack separates its public API from private internals, outside packs can import only from public/, and package.yml declares allowed packs under its dependencies key. CI fails on violations. Writing such checks is real engineering work, but treating them as part of the architecture avoids making enforcement an optional testing investment. For agents, the next question is when that investment produces feedback.

Where in the agent workflow the feedback loop must be placed to work

A fitness function that runs only at merge time leaves the agent outside the correction loop. The human reviewer receives a diff that already violates the architecture, while the agent may have moved on. The same check placed in the verification command after each change makes the failure available during generation. The agent can then use a layer violation in its next iteration without handing the problem to someone else.

Repository instructions such as AGENTS.md or.github/copilot-instructions.md remain useful starting points. They can explain naming conventions, state management patterns, and the requirement that controllers go through services. But prose is a soft rail that the model can ignore under generation pressure. An ArchUnit rule that exits non-zero supplies a computational failure when the agent runs verification.

The “agent sandwich” illustrates where that feedback can be consumed. It pairs planning and generation agents with an adversarial verification agent designed to falsify outputs, at the cost of slower execution than a single-agent workflow. Agents need access to architectural failures while correction remains part of the task. CI at merge is still necessary as a final gate, but it is not sufficient for agent workflows.

Versioning constraints with the code so the architecture cannot silently drift from its rules

A rule outside the repository has no dependable relationship to the code’s history. Even when a separate document preserves its own revisions, the repository does not show which rule governed a particular change. An agent querying the code may receive no rule at all, or encounter outdated guidance elsewhere. Constraints therefore need to version with the code they govern.

This includes architectural test files, dependency-cruiser configuration, import-linter settings, ADR schemas, and constitutional constraint documents. A pull request that changes a boundary can then expose both the implementation and the rule change that permits it. CI can also enforce coverage of new modules by at least one fitness function.

Marri’s Constitutional Spec-Driven Development brings this principle to security through a versioned Constitution with procedures for changing it. Changes to security constraints undergo review and approval as changes to the code do. A repository-native graph model, such as the one Graphide maintains, likewise gives externalized architectural decisions a persistent artifact within the development workflow. The governing architecture is consequently the versioned, enforced rule set.

How Graphide's graph model makes constraint definition and enforcement navigable at agent scale

Before engineers can write rules, they need to know which boundaries to protect. That requires a current map of actual structure. Graphide turns the codebase into a live, editable architecture map covering modules, files, classes, functions, and the calls between them. Plain-English descriptions of nodes make it easier to find a way through the structural view. Engineers can declare constraints against visible relationships rather than reconstructing the whole model from memory.

The workflow starts with gr init, which reads every file and builds the repository graph. Between initialization and checking, gr sync keeps that graph current. The gr check command verifies the graph through a git hook or CI job. It can also sit inside the agent’s verification loop, so the agent and the final merge gate use the same enforcement command.

Layer rules, module isolation, circular-dependency guards, and pattern enforcement all deal with relationships that a live graph can make visible for a machine to check. Graphide provides that navigable foundation for constraint definition and real-time verification. ArchUnit, dependency-cruiser, and import-linter provide other approaches to checking declared boundaries. At the scale of a prompt touching many files, the graph supplies a structural view that a line-by-line diff cannot readily provide. People still have to judge the intent behind the structure, which the graph alone does not explain.

A practical sequence for teams starting to encode constraints today

Use a graph or dependency visualization to distinguish existing boundaries from existing violations, then select the boundary agents are most likely to cross. For most codebases, a layer rule such as controller-to-database or presentation-to-domain is a practical starting point. Encode that rule and establish a passing check through gr check or the equivalent in the chosen tool. Where existing debt blocks adoption, use FreezingArchRule or an equivalent freeze mechanism to separate the baseline from new violations.

Next, link the rule to an ADR containing its stable ID, status, scope, requires/prohibits, and enforcement reference. This gives the prohibition a versioned rationale before enforcement expands. Wire the fitness function into the agent’s verification command as well as the merge gate. As new modules or capabilities arrive, require the same pull request to include a fitness function covering the new surface area.

Sources

  1. Constitutional Spec-Driven Development:Enforcing Security by Construction in AI-Assisted Code Generation
  2. RAD-AI: Rethinking Architecture Documentation for AI-Augmented Ecosystems
  3. Architecture decision records for coding agents · AsDecided
  4. A structured schema for agent-readable architectural decisions

More in Architecture Integrity