# Reviewing against your architecture: teaching the model where the boundaries are.

> An LLM reads your diff but not your layering rules. CommitBrief reads a sibling linter's architecture.json — one-way, read-only — and hands the model your declared import boundaries so it can flag a diff that crosses one.

Published September 23, 2026 by CommitBrief (https://commitbrief.com)

Canonical URL: https://commitbrief.com/blog/reviewing-against-your-architecture/
Tags: architecture, boundaries, engineering

---

An LLM reviewing a diff sees the change and, if you've written one, your [`COMMITBRIEF.md`](/blog/commitbrief-md-anatomy) rules. What it does not see is your architecture. It can't know that a new `import "internal/db"` inside a `domain` file is a layering violation, because nothing ever told it that `domain` is not allowed to depend on `db`. The rule lives in your head, or in a linter's config, or in a wiki page nobody reads — never in front of the model at the moment it's judging the change.

The [previous post](/blog/catching-flaky-tests-before-merge) was about feeding the review a deterministic pre-pass. This one is about feeding it *context* a plain diff can't carry: your project's declared architecture, read straight from the tool that already owns it.

## The bridge, not the linter

There's a sibling tool, [archlint](https://github.com/muhammetsafak/archlint), that lints a repo's import graph against an `architecture.json`: layers are sets of path prefixes, and `rules` declares, per layer, which layers it's allowed to import. archlint fails the build on any import that crosses a forbidden boundary — deterministically, no AI, the same answer every run.

CommitBrief doesn't duplicate that. It **reads** the same `architecture.json` and renders a compact, deterministic summary of the declared layers and their allow/deny edges into the review prompt, as a distinct block:

```
<architecture_constraints>
Layers (by path prefix):
  domain   → internal/domain/
  db       → internal/db/
Allowed imports:
  domain → (none)
  db     → domain
Forbidden: domain → db
</architecture_constraints>
```

That block slots into the system prompt between your `<project_rules>` and the `<severity_rubric>`, so when the model sees a diff that adds `import "internal/db"` to a `domain` file, it has the vocabulary to say *"this crosses the domain → db boundary your architecture.json forbids."*

The relationship is deliberately **one-way**. CommitBrief reads archlint's public config; it never walks the import graph, never lints, never enforces. archlint owns enforcement — it's the deterministic gate. CommitBrief only *informs the reviewer*. Two tools, one source of truth, no overlap in responsibility.

## Read-only, opt-out, and invisible when absent

The feature is on by default and needs no setup beyond having an `architecture.json` in the repo. Three ways to steer it:

```sh
# Skip architecture context for one run.
commitbrief --staged --no-architecture

# Disable it in config (repo-local or user-global).
commitbrief config set review.architecture false

# Point at a non-default location.
commitbrief config set review.architecture_file config/arch.json
```

If there's no `architecture.json` — or it's malformed, or empty — the feature is a **transparent no-op**. It emits nothing, the review runs exactly as it would have, and nothing breaks. A missing config file is never an error; a review tool that fell over because a sibling tool's optional file was absent would be a bad neighbour.

## The cache falls out of it for free

There's a small design detail I'm fond of. The architecture summary folds into the system prompt, and the review cache key is a hash of that prompt. Two consequences drop out with no extra machinery:

- **Editing `architecture.json` invalidates stale reviews automatically.** Change a boundary rule and the prompt changes, so the hash changes, so the next review re-runs instead of serving a cached answer built against the old rules. You don't have to remember to clear anything.
- **A repo with no `architecture.json` produces a byte-identical prompt to the pre-feature version.** The block emits nothing when absent, so the hash is unchanged and every existing cache entry stays valid. Shipping the feature invalidated nobody's cache.

That's the same "the prompt is the seam" property the [provider abstraction](/blog/provider-fallback-strategy) leans on — put the meaningful state in the prompt and the cache key tracks it for you.

## Where it applies

Architecture context is scoped to the review paths — `commitbrief` and `commitbrief dry-run`. The PR-review and eval paths pass an empty architecture context deliberately: a PR review shouldn't depend on the reviewer's local `architecture.json`, and the eval harness needs a fixed prompt to score against. Keeping the scope narrow means the feature adds signal exactly where it helps and stays out of the way everywhere else.

The honest limitation: this is only as good as your `architecture.json`. If your boundaries aren't written down, there's nothing to hand the model, and it's back to reviewing the diff blind to your layering. The feature doesn't invent your architecture — it carries the architecture you've already declared to the one place it was never available before: the reviewer's context window, at the moment of judgement.

The next post shifts from what goes *into* a review to a new way of *calling* one: an MCP server that exposes the whole pipeline as a tool an agent can invoke over stdio.