Turn Architecture Decisions into Constraints Coding Agents Can Be Reviewed Against

A practical method for converting ADRs into path-scoped constraints, routing conflicts to a question, a human reviewer, or a merge check, with a readiness checklist.

Connectory team|12 min

An agent opened a pull request this morning using the HTTP client your team retired eighteen months ago. Tests pass. The diff is clean. The reasoning for the retirement sits in a closed pull request nobody will open again.

To make an architecture decision reviewable against agent output, the record needs four things a typical ADR lacks: a binding statement in imperative form, the paths or boundaries it governs, a detectable signal that a change crossed it, and a defined response when it does (ask the author a question, require a named human reviewer, or fail a merge check). The rest of the record stays prose for people.

This article describes how to get there without building a rules engine, using mechanisms you already have in GitHub plus a small amount of discipline in how decisions are written.

The Decision Your Agent Keeps Relitigating

The failure is rarely that the decision was never made. It is that nothing connects the decision to the files a change touches. A human contributor eventually learns the convention by being corrected in review. An agent starts every task with whatever context it is handed, so it re-proposes the rejected option indefinitely.

It helps to separate three states a decision can be in:

- Undocumented: the reasoning exists only in chat threads, closed pull requests, and two people's memory. Nothing can act on it.

- Documented but unaddressable: there is a readable ADR in docs/decisions/, but no path scope, no signal, and no response. Anyone reading the repository can find it, and nobody is required to.

- Documented with a defined response: the decision names the paths it governs and what happens when a change crosses them, so a conflict surfaces during review rather than during an incident.

Most teams I work with have plenty of the second state and almost none of the third. The gap between them is small, and closing it is mostly editorial, not technical.

Why ADRs Alone Do Not Constrain Anything

The Architectural Decision Record community publishes templates covering context, the decision, alternatives considered, and consequences, kept as version-controlled Markdown next to the code it governs [1]. That is the right content. It makes the reasoning retrievable by whatever reads the repository, including an agent with repository access.

What it does not provide is a review hook. A decisions directory is a library, not a control. Nothing in the template tells you which files the decision applies to, how you would detect a violation in a diff, or who decides when the decision no longer fits.

Standing instruction files narrow part of the gap. GitHub documents repository custom instructions as a way to supply persistent guidance to Copilot instead of restating constraints in each prompt [2]. Use it. But treat it as guidance, not enforcement: an instruction file is advisory, and a stale instruction file misdirects an agent more quietly than a stale wiki misleads a new hire, because the agent never says "that seems out of date."

So the practical gap is three-part: no path binding, no conflict routing, and no record of what happened when the constraint was crossed. The rest of this article addresses those three in order.

Anatomy of a Reviewable Constraint

Keep the long rationale in prose. Only four fields need to be machine-addressable, and they belong in front matter so tooling and agents read the same values a reviewer reads.

yaml
---
id: ADR-0142
title: Service-to-service auth uses mTLS workload identity
status: accepted
date: 2026-01-14
supersedes: ADR-0071
owner: platform-identity
binding_statement: >
  Do not introduce or extend shared session-token auth between services.
  Use the workload identity client in platform-auth v4 or later.
affected_paths:
  - "services/*/internal/auth/**"
  - "**/go.mod"
  - "deploy/**/istio-*.yaml"
signal: import of platform-auth/session OR dependency on legacy-session-go
enforcement: owner_review
exception_owner: platform-identity-lead
---

Write the binding statement in imperative form. The same sentence then serves three readers: the agent that consumes repository instructions, the human reviewer deciding whether a diff complies, and the check author deciding what to detect. If you cannot phrase the decision as an instruction, you probably recorded a preference rather than a decision.

The signal field is the honesty test. If you cannot describe what a violation looks like in a diff, a dependency manifest, or a config file, then the correct enforcement level is a question to the author, not a gate.

Decision shapeWhat it gives youWhat it cannot doBest for
Narrative ADRContext, alternatives, consequences in readable proseCannot be matched to a diff or routed to an ownerHistorical reasoning, org-wide direction
Path-scoped ADRLinks the decision to directories, manifests, and boundariesStill relies on a reviewer noticing the conflictConvention decisions with clear file locality
Path-scoped ADR with enforcement responseBinding statement, scope, signal, owner, and exception pathNeeds maintenance when paths or platforms moveDecisions agents repeatedly violate
Instruction file onlyFast standing guidance for agents, low authoring costNo audit trail, no owner, drifts silentlyA pointer layer on top of real records

Binding Decisions to the Paths They Govern

CODEOWNERS is the cheapest path-to-people binding you already have. GitHub generates review requests from the files a pull request touches [3]. If your ADR has an affected_paths field, mirror it against CODEOWNERS entries so the owner of a path is also the owner of the decisions governing that path. When those two drift apart, decision conflicts land on people with no history to judge them.

Rulesets carry the enforcement half. GitHub documents rulesets as inspectable branch and tag configuration that can require pull requests, require specific status checks, and require review from code owners [4]. That means "which constraints are binding on this branch" is a readable artifact rather than a claim in a meeting. Code scanning alerts surfaced on the pull request can serve as another signal source for decisions with a detectable pattern [5].

Different decision types need different scoping:

- Boundary decisions need directory globs, usually at the module or service directory level.

- Dependency decisions need manifest-file scoping: package.json, go.mod, pyproject.toml, lockfiles.

- Data-handling decisions need both a path scope and a signal, because the violation is often a field name or a log call rather than a file location.

- Cross-repository decisions cannot use paths at all. They need a stable decision identifier that every affected repository references, which is the subject of the example below.

Review boundary
List the repositories, branches, and path globs each decision governs
Evidence source
Record whether the signal comes from a diff pattern, a manifest, or a scanning alert
Exception owner
Name the single person who can accept a conflict and where that acceptance is recorded
Revisit trigger
Tie re-review to dependency and platform upgrades, not to a calendar quarter

Three Responses to a Conflict: Question, Review, Gate

Not every conflict deserves a block. Over-gating is the fastest way to teach engineers that bypass requests are a normal part of shipping, and once that habit forms your gates stop meaning anything. DORA's capabilities catalog treats fast flow and reliable delivery as joint outcomes rather than a tradeoff you resolve by adding friction [8].

Pick the response from the quality of your signal and the blast radius of the change, not from how strongly you feel about the decision.

Conflict typeSignal qualityResponseOwner
Ambiguous convention, small blast radiusHeuristic or inferredAsk the author which decision appliesPull request author
Boundary crossing with local historyDetectable, needs judgmentRequire review from the path code ownerCode owner of affected path
Retired dependency or patternDeterministic (manifest or import)Required status check on protected branchPlatform or domain owner
Data-handling or auth constraintDeterministic, high blast radiusGate plus named exception approverSecurity or identity lead
Superseded decision still referencedDeterministic on decision IDReview with the superseding record attachedDecision owner
Default Everything to a Gate and You Will Lose the Gate
When every decision conflict blocks merge, bypass becomes routine and the audit trail fills with approvals nobody read. Reserve required status checks for conflicts with a deterministic signal and a defined blast radius. Route the rest to a question or a code owner, and keep the bypass list short enough that each use is worth reviewing in the organization audit log [7].

A Cross-Repository Example: One Retired Auth Pattern, Nine Services

A platform team supersedes a shared session-token pattern in favor of workload identity. Nine service repositories still reference the old pattern. Three of them have agent-authored pull requests open that extend it, because the retired pattern is still the most common example in those codebases.

The tempting fix is to copy the ADR into all nine repositories. It fails predictably. Nine copies drift. Superseding one copy does not supersede the rest, and six months later an agent reads whichever copy its repository happens to contain. You now have nine versions of the truth and no way to tell which one a reviewer applied.

The shape that works is one canonical record with a stable identifier, referenced rather than duplicated. ADR-0142 lives in the platform decisions repository. Each service repository references that identifier in two places: its instruction file, so agents receive the binding statement as standing guidance [2], and its enforcement configuration, so the check or owner-review requirement names the same decision.

Walk the conflict path for one service. The agent proposes extending the session-token helper. Review surfaces the superseding record and its binding statement, attached to the pull request rather than recalled by a reviewer. The code owner for services/billing/internal/auth/ then makes one of two decisions: adopt the workload identity client, or file a time-boxed exception that names the reason, the owner, and the removal date. Either way, the outcome is written down.

BeforeAfter
Where the decision livedClosed pull request and a Slack threadCanonical record with ID ADR-0142
How agents encountered itNot at all, unless pasted into the promptInstruction file references the binding statement [2]
Who noticed the conflictWhichever reviewer happened to rememberPath code owner, routed via CODEOWNERS [3]
What the gate requiredTwo generic approvalsRequired check plus owner review on auth paths [4]
Evidence remainingApproval timestampsDecision reference, reviewer, exception record and expiry

The ADR Readiness Checklist

Run this against your existing decisions directory. It takes about half an hour for twenty records, and the result tells you which decisions are actually reviewable.

1. Binding statement written in imperative form, not as narrative preference.

2. Affected paths listed as globs, manifests, or an explicit "cross-repository" marker.

3. Superseding chain present: supersedes and superseded_by rather than edits to the original.

4. Named owner who holds the decision, matching the CODEOWNERS entry for those paths [3].

5. Detectable signal described concretely, or the record is explicitly marked judgment-only.

6. Enforcement level chosen from question, owner review, or required check.

7. Exception path naming who can accept a conflict and where the acceptance is recorded.

8. Stable identifier usable from other repositories without copying the file.

9. Revisit trigger tied to dependency or platform upgrades, not a quarterly calendar.

10. Linked evidence: the incident, benchmark, or audit finding that produced the decision.

Backfill selectively. Write records for the decisions agents actually trip over: dependency choices, retired patterns, module boundary rules, and data-handling constraints. A full historical archive is a project nobody finishes, and it buries the ten decisions that matter under two hundred that do not.

The field most teams omit is enforcement level. Without it, every conflict is resolved by whoever reviews that day, which is exactly the inconsistency the record was supposed to remove. Decide the response when you write the decision, while the rationale and blast radius are still fresh. The same discipline applies when you later add controls mapped to secure-development practices such as those in NIST SP 800-218 [9], or build provenance against the SLSA levels model [10].

Where Connectory Fits This Workflow

Cross-repository referencing needs decisions to exist as typed records with stable identifiers rather than files duplicated per repository. That is what Genie's institutional memory layer holds: policies, decisions, ownership, constraints, open questions, and the evidence behind them, server-side and addressable from any repository that references them.

Surfacing the conflict is a review problem. SlopBuster reviews a pull request against repository context and recorded organizational intent, so a change that contradicts an accepted decision appears as review feedback with the record attached, not as a reviewer's recollection. If you want the reasoning behind context-aware review versus diff-only review, the comparison with traditional review covers it.

The gate and exception half belongs to Guardian's merge gate enforcement: which conflicts block, who may grant an exception, and what record that exception leaves. Choosing gate strength by blast radius is worth planning deliberately, and risk-weighted merge gates walks through that calibration.

All of this sits alongside CODEOWNERS, rulesets, protected branches, and code scanning rather than replacing them [3][4][6][5]. And the boundary stays human: the tooling surfaces the conflict, attaches the decision, and records the outcome. A named owner still decides whether the decision holds, bends, or gets superseded.

Frequently Asked Questions

Do we need ADRs if we already have a repository instruction file?

Yes. The instruction file is a short, maintained pointer to binding constraints; the ADR holds the rationale, alternatives, and consequences [1]. Keep guidance and reasoning in one layer with two parts, and update the pointer whenever a decision is superseded [2].

How do we stop agents from reading superseded decisions?

Never edit a decision in place. Mark the old record superseded, write a new one, and have the instruction file reference only accepted records. Agents then read one current truth while the reasoning history stays available to people.

Should decision conflicts be a required status check?

Only when the signal is deterministic and the blast radius is defined. Rulesets let you require specific checks on protected branches [4], which is the right tool for retired dependencies and auth constraints. Judgment-heavy conflicts belong in code owner review.

Who owns a cross-repository decision?

The team that published it, with a named exception approver. Service teams reference the identifier; they do not fork the record.

What to Do Next

Pick the one decision your agents violate most often. The retired HTTP client, the deprecated config layer, the data-access shortcut you thought you had killed. Add four fields to its record today: binding statement, affected paths, signal, enforcement level.

Then mirror those paths against CODEOWNERS so the decision and the reviewer travel together [3], and reference the record from your repository instruction file so agents receive it as standing guidance [2].

Start tracking one signal this week: how many pull requests reference a decision record versus how many conflicts a reviewer catches from memory. When the first number climbs and the second falls, the decision has stopped being prose and become a constraint. That clean-looking pull request from this morning is the test case. Make it the last time that decision gets relitigated.

References

[1] Architectural Decision Records, "ADR templates and practices," 2026. https://adr.github.io/

[2] GitHub Docs, "Add repository custom instructions for GitHub Copilot," 2026. https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-custom-instructions

[3] GitHub Docs, "About code owners," 2026. https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners

[4] GitHub Docs, "About rulesets," 2026. https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets

[5] GitHub Docs, "About code scanning," 2026. https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning

[6] GitHub Docs, "About protected branches," 2026. https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches

[7] GitHub Docs, "Reviewing the audit log for your organization," 2026. https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-organization-settings/reviewing-the-audit-log-for-your-organization

[8] DORA, "Capabilities catalog," 2025. https://dora.dev/capabilities/

[9] NIST, "SP 800-218: Secure Software Development Framework," 2022. https://csrc.nist.gov/pubs/sp/800/218/final

[10] SLSA, "Specification v1.0: Security levels," 2025. https://slsa.dev/spec/v1.0/levels