On this page
Boundary Rules
A boundary rule is a dependency rule you declare over a board's nodes: an app's modules on its app board, or your systems on the root board. The code plugin checks app board rules against the repo on every sync, and ProvenMap checks root board rules against the root board's edges. Use rules to write down the layering and data ownership you intend, then see exactly where your systems or your code cross them.
Declare a rule
Rules live on a board's Aspects screen, under the Boundaries tab. A rule's nodes come from the board you declare it on. To rule over the nodes inside a layer, open that layer first; to rule over your systems, open the root board.
- Open Aspects on the app board, layer or root board, and choose the Boundaries tab.
- Choose Add rule. The New rule form opens beside the list.
- Enter a Name. The Slug fills in from the name. Change it now if you want to; it's fixed once saved.
- Choose a Kind and pick the nodes on each side (below). The form reads the rule back as a sentence, such as "Only Ordering Infrastructure may depend on Ordering DB".
- Add a Rationale: why the boundary exists.
- Choose Add rule, then Save changes at the top of the screen.
Save changes saves your rule edits into your working copy, like other board edits. On an app board, a new rule shows Not yet evaluated until the next sync checks it. On the root board, it's checked as soon as it's saved.
The two kinds
Over a store node, Only … may depend on is data ownership: only the nodes you allow may use that store. The node pickers list every node on the board, including stores and external systems, and each side takes up to 50 nodes.
From chat or the workspace plugin
Chat and the workspace plugin can declare rules too. Ask for one in chat or in /board, and the rule is written with the author_boundary_rules tool. An external agent with a read + write MCP token uses the same tool.
Rules between systems
The root board's nodes are your systems: services, stores, brokers and the apps they run as. Rules declared there say how systems may depend on each other. For example: "Only Ordering API and Order Processor may depend on orderingdb", or "Web App must not depend on Payment Processor".
No repo is bound to the root board, so ProvenMap checks its rules itself. The evidence is the root board's edges and the calls your apps' code makes to each other, from each app's api.clients aspect. A rule is checked when you save it, each time a commit changes the root board, and each time an app's push lands. A new edge to a store shows up as a crossing as soon as you commit it, and a new call as soon as its app pushes. Each crossing names its evidence: the edge that crosses the rule, or the client that makes the call.
When /setup draws your landscape from a .NET Aspire AppHost, it also offers one data-ownership rule per database, naming the systems wired to it today. Pick the ones to declare.
Drawing your landscape in chat ends with the same offer, once the landscape is saved: one data-ownership rule per store, naming the systems with an edge into it. Describe a separation you want, such as "the storefront never calls payments directly", and chat declares it as a rule too. A missing edge alone never becomes a rule.
See the rules as rings
The Boundaries tab has two views: List and Rings. Rings draws the board's rules as an onion of stacked rings, seen from above.
- The rings come from your rules. A Must not depend on rule puts the first nodes inside the second, so every dependency a rule allows points inward. The nodes your rules keep most independent, such as a domain model, sit at the centre.
- Owned resources stand outside the rings. The target of an Only … may depend on rule is drawn as a dashed card, next to the nodes allowed to reach it.
- Arrows are dependencies. A solid arrow is one a rule allows. A dashed red arrow with ✕ and a count is a crossing that breaks a rule. Hover a node to light its arrows; choose an arrow to open its rule.
- Two rules that contradict each other put nodes inside each other. Those nodes share one ring, and a warning above the rings names the rules.
The rings show only the rules declared on the board you're on. On an app board, rules declared on its layers are listed but drawn on their own layer's rings.
Read a rule's status
Each rule shows one of three statuses.
Each violation is a crossing from one node to another, such as Ordering API → Ordering DB, with a count of the evidence behind it. Expand a crossing to see that evidence:
- An import: the file, then the file it imports
- A project reference (.NET): the project, then the project it references
- A board edge: the source node, the target node and the edge's relation
Each crossing shows up to 20 pieces of evidence and lists the rest as "more not sampled". The count always covers all of them.
Watch rules from the hub
The Boundaries card shows on an app's hub by default. On the root hub, add it from Add widget. It counts the rules and how many hold, are violated or are not yet evaluated.
Seen in Changes
Each week of an app's Changes diary records the rules that moved: one that broke, one that broke and was fixed inside the week, and one that holds again. A rule still broken when the week ends is a flag to review, so a crossing an agent made shows up beside the work that made it. The root board's rules aren't in a week: they are proven without a repo.
Edit or delete a rule
Open a rule and choose Edit or Delete. You can change the name, the kind, the nodes and the rationale, but not the slug. To delete several rules at once, tick them in the list and choose Delete in the bar that appears. Save with Save changes, as when you declare one.
Changing the kind or the nodes resets the rule to Not yet evaluated. Its note reads "The rule changed; it is evaluated again next." Renaming a rule or editing its rationale keeps its status.
The Boundaries tab on an app board also lists the rules declared on its layers. A rule declared on a layer has its name, kind and nodes edited from that layer; elsewhere, only its Rationale can change.
How the code plugin checks a rule
The code plugin checks every rule on a board at the end of each successful /sync of that board. There is nothing to configure. It checks three kinds of evidence:
- Imports between files, from the dependency graph that
/analyzebuilds - Project references in each
.csprojfile, for .NET code - The board's own edges, for nodes that have no files, such as databases and external systems
/sync prints the result, then one line for each rule that is violated or not evaluated:
The plugin reports a violation; it never changes code to fix one. When you sync from a branch other than the one the board's binding is pinned to, the check is skipped.
Track a fix with a work item
A work item anchored to a rule tracks that rule's status. Open the rule and click the ⊕ in its header to add it to the work item you're editing. The rule's row then shows the work items anchored to it.
When a sync moves the rule between Holds and Violated, the work item shows ≠ Drifted. A developer who pulls the work item with /work-items is told the change is done when /sync reports the rule holding. Each sync until then lists the imports and project references that still break it.
Publish rules with a board
When you publish a board, the wizard's Contents step lists your rules under Aspects, in the Boundaries group. A reader sees each rule you select: its sentence, its status, when it was evaluated, its rationale, and each crossing with its count. The file paths, projects and commits behind a crossing are never published. Publishing Boards covers the wizard.
FAQ
Why does a rule still say Not yet evaluated after a sync?
Hover the mark or open the rule to read the note. It gives one of three reasons:
- The rule names a node that isn't on the board. On the root board, this is the only reason.
- The repo has no dependency graph yet. Run
/analyze, then/sync. - The rule changed after the sync read it. The next sync checks it.
Why does a rule hold when I know the code crosses it?
A file counts for a node only when the board maps that file to the node. An import from a file that no node on the board covers isn't counted against any rule. If the board is behind the code, run /analyze and /sync again.
What happens to rules when I remove the app?
Remove app keeps them. A plain layer has no Aspects screen, so the rules show again when you make the board an app again. Delete app removes them with the board. See Removing or deleting an app.
What's next
Last updated November 6, 2026

