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:

  1. In the repo, run /analyze, then /sync, with the code plugin.
  2. Run /adopt --aspect <kind> for each kind you want:
TabWhat it holdsCommand
DatabaseTables, columns & relationships/adopt --aspect database.schema
EndpointsHTTP endpoints & their contracts/adopt --aspect api.surface
ClientsOther services this code calls/adopt --aspect api.clients
EventsChannels, events & subscriptions/adopt --aspect event.catalog
AuthZRoles, permissions & policies/adopt --aspect authz.registry

Two kinds work differently. Boundary rules are declared on the board rather than extracted. An app's routes, /adopt --aspect ui.pages, have their own Pages screen.

Until a kind is adopted, its tab says so and names the command.

The kinds

The Aspects rail lists the kinds in the order below. Select one to see its rows; select a row to open it in the inspector on the right.

Database

The tab draws the schema as a diagram: each table with its columns, and a line for each foreign key. Open a table to see its dialect, ORM, primary key, estimated rows, columns, foreign keys and indexes.

You can mark a table deprecated, and give each column a Classification: public, internal, confidential, pii or secret.

Endpoints

The tab groups the endpoints by method, as cards sorted by path. A card shows the path and the auth the endpoint needs: public, an API key, a bearer token or OAuth scopes. It also lists the parameters, response codes, rate limit, paging and any sunset date. Select the path to find the endpoint's owner node on the canvas.

Called by lists the other apps whose code calls the endpoint, read from the root board's call map. You can mark an endpoint deprecated.

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 groups the event channels by broker. A card shows the channel's ordering, delivery guarantee, dead-letter queue, and how many producers and consumers it has.

Open a channel for its event types, subscriptions, producers and consumers. You can 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

Boundaries

The rules you declare for which nodes may depend on which, each checked against the code. See Boundary rules.

Annotate an aspect

Whatever the code plugin extracts, the code decides, so most fields are read-only. Hover a locked field to see why: "Derived from source analysis — edit it in the code and re-sync."

What you can change is the judgment the code doesn't hold: deprecating a table or an endpoint, classifying a column, a channel's owners and notes, a rule's rationale. Changes wait in the save bar; Undo and Redo step through them, and Save changes writes them. Only rows you created, such as a boundary rule, can be deleted. A row the code plugin extracted goes away the next time you adopt, once the code no longer has it; /adopt --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 in the hub's Add widget menu: Database, Endpoints, Pages, Events, AuthZ, Boundaries and Clients.

A kind's card leads with its count and a status line. At three rows high it adds a few numbers and a small chart. Taller, it lists the rows that need a look, worst first, and counts any it can't fit. With nothing needing a look, it lists the kind's top rows instead. Select a row to open it.

CardIts numbersWhat needs a look
DatabaseNo primary key · Partitioned · DeprecatedTables with no primary key
EndpointsPublic · Deprecated · Rate-limitedDeprecated endpoints, with any sunset date
PagesGuarded · With forms · Unguarded writesPages that write data with no page guard
EventsEvent types · Subscriptions · No dead-letterChannels with no dead-letter queue
AuthZPermissions · Policies · Resource typesRoles with no permissions and no parent role
BoundariesHolds · Violated · Not yet evaluatedViolated rules, most crossings first
ClientsHTTP · gRPC · Calls—

Only a violated boundary rule is marked as a warning, because a check proved it. The other looks are worth a question, not a verdict: an unguarded page may still be protected by its API, for instance.

An empty card says what it will show and the step that fills it. That's the /adopt 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 is the map of your systems, so its Aspects screen shows only what passes between them: Clients, Events and Boundaries. Clients and Events are folded from every app's own aspects each time you open them. See Aspects Across Systems.

FAQ

Why can't I edit an endpoint's path or a table's columns?

They come from the code, and the next /adopt would put them back. Change them in the code, then adopt again.

Does

No. /adopt never touches the rows you created or the fields you can edit, so your classifications, owners and notes survive every adoption.

A kind's hub card is empty, but the repo is bound. Why?

That kind hasn't been adopted yet. The card names the command; run it in the repo.

What's next