On this page
Aspects
An aspect is a typed detail of your architecture, such as a table, an endpoint or a rule, attached to the node it belongs to. You use aspects to see what an app's code contains, to annotate it, and to anchor work items to a specific table or endpoint.
An app board's Aspects screen lists them by kind. The hub counts them and, card by card, shows what needs a look.
Set up
The code plugin extracts aspects from the repo bound to the board. The board has to be mapped first, because every aspect attaches to a node:
- In the repo, run
/analyze, then/sync, with the code plugin. - Run
/aspects --aspect <kind>for each kind you want, or bare/aspectsto pick several:
Two kinds work differently. Boundary rules are declared on the board rather than extracted. An app's routes, /aspects --aspect ui.pages, have their own Pages screen.
Until a kind is extracted, its tab says so and names the command.
Changed 10 October 2026: the command is /aspects, with the same flags. /adopt only points to it.
The kinds
Database
The tab shows the schema two ways. List puts every table under its domain, and Diagram draws each table with its key columns and a line for each foreign key. A board with more than 20 tables opens on List, and the view you pick is remembered for that board in your browser.
Tables group by domain, the part of the business each belongs to, such as billing. A domain is a context tag named domain:<name>, such as domain:billing, on the node that is that business area. A table takes the domain tag of the node whose code uses it. When that node has none, it takes the tag of the first node above with one: its container, then the node that drills into its board.
When the code names a table's domain outright, such as a Prisma @@schema, that name wins. Tables with no domain share No domain.
Changed 26 October 2026: the Domain field on a node no longer moves tables; the node's domain: tag does.
To move tables to another domain, change the domain: tag under the node's Tags; the tables regroup the next time /aspects runs. Renaming the tag in Organization settings renames the domain on every node and table at once. The code plugin tags each business-area node during /sync, and a tag you add to a node yourself wins over the one it pushed.
In List, each domain shows its tables with their column counts and names the other domains its foreign keys reach, under Uses and Used by. Choose a domain's Diagram to see that domain drawn.
In Diagram, each domain is a box labelled with its name and table count; choose the name to zoom to it. Tables no foreign key touches sit at the bottom of their box under No foreign keys. A foreign key into another domain draws no line until you select one of its tables: the column names the table it points at, and choosing that name goes there. Zoomed out, each table shows only its name.
Under Group by, Schema boxes the tables by database schema and None draws the schema as one diagram. Both Domain and Schema show only when they split the tables, so a board whose tables share one schema has no Schema tab.
In Diagram, each table shows its primary, unique and foreign-key columns. To see every column on the canvas, choose the line at the foot of the table, such as Show 11 more columns. Its domain's box grows to fit it, and Show fewer columns folds it again. A column most tables carry, such as workspace_id, shows as a mark beside the table's name until you expand the table.
Find a table or column fades the tables that don't match, in either view; press Enter to step through the matches.
Filters hide tables, where search only fades them. Use All domains to pick the domains to show. Use Filter to keep only the tables With foreign keys or Without foreign keys, or those matching Without workspace_id, JSON columns or No primary key. An option shows only when some table matches it. Each of those filters appears as a chip under the bar while it is on: remove it there, or choose Clear filters to bring the whole schema back.
Select a table to open its detail. References and Referenced by list the tables it joins to and through which columns. Its columns follow, with the audit columns listed apart under them, and then its indexes. Choose a table's name to open it.
You can mark a table deprecated, and give each column a Classification: public, internal, confidential, pii or secret. Choose Classify data above the columns to set them, then Done. A sensitive classification stays marked on its column.
Endpoints
The tab is a grid: one row per path, one column per method, so every operation on a path sits on one row. Each cell's mark says who can call that operation:
Above the grid, a bar shows how much of the API falls under each mark; choose a part of it to show only those endpoints. The count of public routes that change data, such as 12 public routes change data, shows only the orange ones. The most common rate limit is stated once above the grid, and the Own limit column shows a row's limit only when it differs. Rows group by path prefix; under Group by, Module groups them by the node that owns them instead, and Domain by the domain each endpoint belongs to.
Select a cell to open the endpoint. Its domain shows under the path. Its Who can call it section names the credential, any role it needs and its rate limit. Its Request section lists the parameters and body, and Responses the status codes. For an operation that changes data, Retries says if a retry is safe.
The endpoint's Called by section lists the other apps whose code calls it, read from the root board's call map. Select the owner's name to find its node on the canvas. You can mark an endpoint Deprecated and give it owners and notes.
Clients
The tab lists the other services this app's code calls, grouped by the service they call. Open a client to see its base address, its name in the code, and the calls it names. A client that names no call depends on the whole service.
Events
The tab shows each channel as a lane that reads left to right. The lane's From side lists the nodes that publish on it. Then come the channel and its event types, and the Delivered to side lists the nodes that receive each type. An event type nothing in the app subscribes to says No subscriber in this app. Broker narrows the lanes to one broker.
When channels belong to more than one domain, the lanes group under each domain's name, with No domain last; under Group by, None shows one list. A dead-letter queue stays under the channel that sends failures to it, whatever its own domain.
When the app has no producer for a channel, the lane says where its messages come from, as far as the channel itself says:
- Arrives over HTTP at a route, with Open the endpoint when the app serves it, for a webhook
- Fires on a schedule, with the schedule, such as
rate(1 hour) - The rule's filter names the source, such as
aws.amplify
A dead-letter queue sits under the channel that sends failures to it. The lane says how many failed receives move a message there, and Nothing reads these dead letters marks a queue no node consumes.
The line above the lanes counts what needs a look:
- dead-letter queues nobody reads
- channels with no dead-letter target
- event types with no subscriber
- event types with a payload schema
- channels with no recorded delivery guarantee
Select a lane to open the channel. Its domain shows under its name. Transport lists its delivery guarantee, ordering, dead-letter target, redrive, retention and the rest, with Not recorded where the code didn't say. You can mark an event type Deprecated and override a subscription's delivery guarantee under Delivery per subscription. You can also add owners and write the channel's Description, Usage notes, Rationale, Runbook URL and Notes.
AuthZ
The tab shows the board's authorization registry in three views:
- Matrix: roles against resource types, with the actions each role may take
- Roles: how roles inherit from each other
- Policies: each policy's source text, as the code declares it
AI
The tab lists what the app's code runs as AI, in three sections: Agents, Tools and Model calls. Open an agent for its Models, the Tools it is wired to, who it Hands off to and the skills it Loads. It also shows its Memory, whether a person is in the loop, and Guarded: whether a guardrail or a human step is drawn beside its node. Open a tool for the Inputs an agent must give it, the agents it is Used by and its Transport. Reaches lists the tables, endpoints and clients its handler touches. Open a model call for its Provider, Model, Streaming and Structured output; you can set its Cost tier.
Every row says where it was read, under Declared in. A field the extraction could not cite stays empty. A reference the push could not resolve, a tool an agent names that no row serves yet, keeps its name and is counted until a later push serves it.
Edit or delete an aspect
Each tab offers the fields you can change. You can deprecate a table, an endpoint or an event type, classify a column, and give an endpoint or a channel owners and notes. On a channel you can also set a subscription's delivery guarantee, and on a rule its name, nodes and rationale. Changes wait in the save bar until you choose Save changes.
To delete a row, open it and choose Delete, or tick several rows in the list and choose Delete in the bar that appears. The AuthZ tab is one registry, so its Delete sits beside its Matrix, Roles and Policies switch and removes the whole registry. On the database diagram, hold Shift and drag a box, or ⌘-click, to pick tables; on the endpoints grid, ⌘-click or Shift-click the cells. On Windows and Linux, read ⌘ as Ctrl. A deleted row leaves the board when you choose Save changes; Undo brings it back until then.
A row the code plugin extracted is governed the way a node is. Deleting it, or changing a fact the code decides, is a claim about the code. When you commit, the work item records the change as a proposal and the board returns to what the code says. Owners, notes and deprecation are yours and commit plainly. The next /aspects run restores whatever the code still has; /aspects --mode merge adds rows without removing any.
Each row also has Add to work item, which anchors a work item to that table, endpoint or rule.
Aspects on the hub
The hub's Aspects card counts every kind the board holds. Each kind also has a card of its own. Boundaries shows on an app's hub by default. The others are in the hub's Add widget menu: Database, Endpoints, Pages, Events, AuthZ, Clients, AI agents, AI tools and Model calls.
A kind's card leads with its count and a status line; a taller card lists the rows that need a look, worst first. With nothing needing a look, it lists the kind's top rows instead. Select a row to open it.
Only a violated boundary rule is marked as a warning. The other rows are worth a question, not a verdict. An unguarded page may still be protected by its API, and an agent may be guarded in code the board does not draw.
An empty card says what it will show and the step that fills it. That's the /aspects command to copy, or Draft in chat for boundary rules. On a board no repo is bound to, it asks you to bind one first.
Aspects on the root board
The root board's Aspects screen shows only what passes between your systems: Clients, Events, Boundaries and AI. Clients, Events and AI are folded from every app's own aspects each time you open them. See Aspects Across Systems.
FAQ
Does
No. /aspects never touches the rows you created or the fields you can edit, so your classifications, owners and notes survive every run.
What's next
The Code Plugin
/aspects in full: every kind, its flags, and what it reads.
Boundary Rules
Declare which parts of a board may depend on which, and see every crossing.
Aspects Across Systems
The events and calls that pass between your apps, on the root board.
Pages
An app's routes, captures and guards, on a screen of their own.
Use case · Plan a change
Understand dependencies across multiple repositories
Every app's endpoints, events and tables adopted, then folded on the root into who calls and who subscribes to whom.
Usually an architect or a platform engineer, before a contract changes
Use case · Plan a change
Plan and track a software migration
A monolith's tables, endpoints and channels, extracted with their file and line, become the list its split must account for.
Usually an architect or platform engineer, with the team doing the migration
Last updated November 6, 2026

