magus v0.4.3 is out. See what's new
¶ View generated markdown
9 min read

magus-diagram

Short form: 7241 bytes, 9% smaller than the full form's 8034.

Write, check and view an architecture figure with magus/figure, the embedded Buzz module: boxes built from the knowledge graph's own Dir records, groups over a declared layer or a dirs set, edges derived from imports and declared calls, and a layout nobody places by hand. Use when a doc or review needs a picture of one subsystem, process or package scope, when a figure refuses to draw and names the call to change, and when reading the console's Diagrams page. Do NOT use to draw the whole workspace or to place boxes by coordinate; for Buzz syntax itself use magus-buzz-lang.

Install it, rather than copying from this page:

magus agent install .claude/skills   # writes both forms below

An installed copy carries a provenance stamp, so magus doctor can tell you when a magus upgrade has made it stale. Text copied from this page carries none.

What an installed copy carries

magus agent install writes this frontmatter above the body. magus doctor reads it to report whether your installed skills are current.

field value
license GPL-3.0-or-later
compatibility any-agent
source magus
agent-skill-version 110
knowledge-schema-version 16
skill-content 0c478b39a9d4
skill-variant full

The skill-content digest covers this skill alone, and both forms below report it: they go stale together, never one silently, and a change to another skill does not move it.

The two forms

Both are hand-authored from one source body. The short form is the always-loaded primary - the enumeration dropped, the judgment kept, for the most capable readers rather than the least. The full form is its <name>-full twin, loaded by name when a reader wants the rationale. The bar above shows how much shorter the primary is; switch between them here to see exactly what it gave up. See Skills for how to choose.

magus agent install --tar | tar -xO -f - magus-diagram/SKILL.md
# Drawing architecture figures with magus/figure

`magus/figure` is a Buzz module embedded in the binary that lays out an architecture
figure from the knowledge graph's own records. A box is a directory the graph holds, a
group is a set of them, and the edges between code boxes are the imports and declared
calls those records carry. You never place anything and never type an edge between two
packages. The module places the figure, and refuses one it cannot draw cleanly with a
finding that names the call to change.

## When to reach for it

Draw ONE claim per figure: a subsystem, a process, or a package scope, stated in
the title, with the boxes and connectors as its evidence.

- Reach for it when prose keeps restating how parts connect, when a doc page
  describes a request path or a pipeline, or when a reviewer needs to see which
  packages a change crosses.
- Never draw everything. A figure of the whole workspace blows every budget and
  proves nothing.
- Before writing one, look for an existing figure of the same scope with
  `magus query diagrams`.

Budgets: at most 9 boxes, 12 connectors, 3 zones, 2 accented elements, and 14
characters per connector label.

## The first figure

A figure is one exported function returning a `figure\Figure`. Both imports resolve
in any workspace; nothing needs a copy on disk:

```buzz
import "magus";
import "magus/figure";

export fun serverHttpFigure() > figure\Figure !> any {
    final guard = magus\dir("internal/httpx");
    final mcp = magus\dir("internal/handler/mcp");
    final handlers = magus\layer("handler").dirs;
    final agent = figure\external("AI agents", look: figure\Look.plain);
    return figure\of("server-http", title: "The HTTP surface")
        .box(guard, label: "Guard", focal: true)
        .box(mcp, label: "/mcp")
        .group(figure\without(handlers, drop: [mcp]), label: "Console routes")
        .edgesFromGraph()
        .flowIn(agent, dst: guard)
        .scope(handlers + [guard]);
}
```

`magus\dir(path)` raises MGS7005 on a directory the graph does not hold, naming the
nearest one, and `magus\layer(name)` raises MGS7006 on a layer nothing declares. A typo
fails the figure at the line that made it. `.diagram()` lays it out and raises every
finding at once; `figure\draw(f, theme: figure\Theme.light)` does the same and paints
it. Prove a figure draws before you register it: call it from a scratch `main()` inside
`try`/`catch` and run it with `magus buzz <file>`.

## Boxes, groups and actors

| call | draws |
| --- | --- |
| `.box(dir, label:, sub:, tag:, focal:, look:, symbol:)` | one directory; `symbol:` anchors it at a `magus\refs` result |
| `.group(dirs, label:, ...)` | every directory in a list of `magus\Dir` as one box |
| `figure\external(name, sub:, tag:, link:, look:)` | an actor: a person, a vendor, a step, a Buzz file, a decision |

Take directories as records, never as path strings: `magus\layer("handler").dirs` is
every directory a declared layer covers, `magus\dirs("internal/queue/*")` every directory
a glob matches. `+` joins two lists, and `figure\without(dirs, drop: [...])` leaves some
out, raising when one it drops is not there.
A package that joins the layer joins the group, and nothing in the figure changes.
Layers are declared under `"layers"` in `magus\project`, workspace-relative directory or
glob to a lowercase name.

A directory is drawn once: a box inside a group's dirs is refused, so drop it with
`figure\without`. Files of one package are one box, or actors when they are steps.

`look:` takes `figure\Look.plain`, `focal`, `store`, `external`, `input`, `optional` or
`decision` (actors only, at most three exits). Spend the two accents on the one point the
title makes, and give every look you use a `.legend(figure\Look.x, label:)` line.

## Edges come from the graph

`.edgesFromGraph()` draws every import, and every declared `magus:calls` marker, between
two drawn boxes; two or more code boxes require it. Arrows point importer to imported. An
edge inside one group is not drawn. Every drawn directory needs a symbol index: an
unindexed one is a finding, never an empty edge list, so run `magus graph build` first.

- `.hideEdges(src, dst:, why:)` drops graph edges from one list of dirs to another, reason
  required. A hide that matches nothing is a finding: the code changed, so look again.
- `.markEdge(src, dst:, label:, stroke:)` labels or strokes one graph edge.
- `.flowIn(actor, dst: dir)`, `.flowOut(dir, dst: actor)` and `.flowAcross(actor, dst:
  actor)` are the only hand edges. Each has an actor at one end and claims flow, which
  nothing checks. A hand edge where the graph already draws one is a finding.

## Scope

`.scope(dirs)` names the directories the figure answers for. Every one must be drawn by a
box or a group, or left out with `.except(dirs, why:)`, which is for a true exclusion, not a
list of packages the picture did not fit. Narrow the scope instead. A figure with no code
behind it, a decision process or a CI arrangement, says so with `.unscoped(why:)`.

## Findings name the call to change

Nothing is drawn until the figure is clean. Real ones:

```text
figure "server-http": "internal/server" is in scope("internal/**") but nothing draws it; add it to a box or a group, or except([...], why:)
figure "t": draws 2 boxes of code and no edge between them; call edgesFromGraph()
figure "t": "a" has no symbol index, so its imports are unknown and edgesFromGraph() cannot draw them; build it with `magus graph build`
```

Do what the finding says, at the call it names. A finding ending `layout bug, report it`
is not yours to fix: reorder declarations or split the figure, and report it.

## Register and embed

A figure nobody registers is checked by nothing. Find the workspace's registry with
`magus query diagrams`; in magus's own tree it is `docs/site/diagrams/all.buzz`, one
import plus one entry. `magus run diagrams-generate docs` lays every figure out from the
symbol index and writes the light, dark and page SVGs with a JSON receipt beside each:
the stamp, the claim counts, the findings, and the index digest it was drawn at. A docs
page embeds a figure by id with `<!--diagram:<id>-->`.

Source code can point back at a figure with a `magus:diagram <id>` comment beside the
code a box depicts; a marker whose figure is gone is a finding.

## The console

The console's Diagrams page draws the workspace itself, with no figure file: the
project graph, one project's targets, and the package import graph, each through a
declared lens of scope, focus and depth. It needs a running server
(`magus server start`), and the import view needs the symbol index. The server
renders with the same module, so a lens over the budget is refused with its own
finding: narrow the lens rather than asking for a bigger picture.

## What this skill refuses

- **Coordinates, weights, pins.** A layout knob grows into a second language. Shape a
  figure with `rank`, `row`, `zone` and `boundary`, or split it.
- **Hand-drawn code edges and path strings.** An edge nobody's code holds asserts
  something nobody checked, and a path string goes stale the day a package moves.

Writing the Buzz itself, the syntax and the strict-mode rules: magus-buzz-lang.
magus agent install --tar | tar -xO -f - magus-diagram-full/SKILL.md
# Drawing architecture figures with magus/figure

`magus/figure` is a Buzz module embedded in the binary that lays out an architecture
figure from the knowledge graph's own records. A box is a directory the graph holds, a
group is a set of them, and the edges between code boxes are the imports and declared
calls those records carry. You never place anything and never type an edge between two
packages. The module places the figure, and refuses one it cannot draw cleanly with a
finding that names the call to change.

## When to reach for it

Draw ONE claim per figure: a subsystem, a process, or a package scope, stated in
the title, with the boxes and connectors as its evidence. A figure that
needs more than the budgets below is two figures, an overview and a detail, each
its own `figure\of()`; the budgets are the design system's, and past them nobody
reads the picture.

- Reach for it when prose keeps restating how parts connect, when a doc page
  describes a request path or a pipeline, or when a reviewer needs to see which
  packages a change crosses.
- Never draw everything. A figure of the whole workspace blows every budget and
  proves nothing; the console already draws the whole project graph
  through a lens, so a hand-written copy of it is a second answer.
- Before writing one, look for an existing figure of the same scope with
  `magus query diagrams`; two figures of one subsystem drift apart.

Budgets: at most 9 boxes, 12 connectors, 3 zones, 2 accented elements, and 14
characters per connector label.

## The first figure

A figure is one exported function returning a `figure\Figure`. Both imports resolve
in any workspace; nothing needs a copy on disk:

```buzz
import "magus";
import "magus/figure";

export fun serverHttpFigure() > figure\Figure !> any {
    final guard = magus\dir("internal/httpx");
    final mcp = magus\dir("internal/handler/mcp");
    final handlers = magus\layer("handler").dirs;
    final agent = figure\external("AI agents", look: figure\Look.plain);
    return figure\of("server-http", title: "The HTTP surface")
        .box(guard, label: "Guard", focal: true)
        .box(mcp, label: "/mcp")
        .group(figure\without(handlers, drop: [mcp]), label: "Console routes")
        .edgesFromGraph()
        .flowIn(agent, dst: guard)
        .scope(handlers + [guard]);
}
```

`magus\dir(path)` raises MGS7005 on a directory the graph does not hold, naming the
nearest one, and `magus\layer(name)` raises MGS7006 on a layer nothing declares. A typo
fails the figure at the line that made it. `.diagram()` lays it out and raises every
finding at once; `figure\draw(f, theme: figure\Theme.light)` does the same and paints
it. Prove a figure draws before you register it: call it from a scratch `main()` inside
`try`/`catch` and run it with `magus buzz <file>`.

`title` is the claim, `desc` the sentence under it, `eyebrow` the small label
above, all arguments of `figure\of`. `direction: figure\Direction.down` lays ranks top to
bottom instead of left to right.

## Boxes, groups and actors

| call | draws |
| --- | --- |
| `.box(dir, label:, sub:, tag:, focal:, look:, symbol:)` | one directory; `symbol:` anchors it at a `magus\refs` result |
| `.group(dirs, label:, ...)` | every directory in a list of `magus\Dir` as one box |
| `figure\external(name, sub:, tag:, link:, look:)` | an actor: a person, a vendor, a step, a Buzz file, a decision |

Take directories as records, never as path strings: `magus\layer("handler").dirs` is
every directory a declared layer covers, `magus\dirs("internal/queue/*")` every directory
a glob matches. `+` joins two lists, and `figure\without(dirs, drop: [...])` leaves some
out, raising when one it drops is not there.
A package that joins the layer joins the group, and nothing in the figure changes.
Layers are declared under `"layers"` in `magus\project`, workspace-relative directory or
glob to a lowercase name.

A directory is drawn once: a box inside a group's dirs is refused, so drop it with
`figure\without`. Files of one package are one box, or actors when they are steps.

`look:` takes `figure\Look.plain`, `focal`, `store`, `external`, `input`, `optional` or
`decision` (actors only, at most three exits). Spend the two accents on the one point the
title makes, and give every look you use a `.legend(figure\Look.x, label:)` line.

## Edges come from the graph

`.edgesFromGraph()` draws every import, and every declared `magus:calls` marker, between
two drawn boxes; two or more code boxes require it. Arrows point importer to imported. An
edge inside one group is not drawn. Every drawn directory needs a symbol index: an
unindexed one is a finding, never an empty edge list, so run `magus graph build` first.

- `.hideEdges(src, dst:, why:)` drops graph edges from one list of dirs to another, reason
  required. A hide that matches nothing is a finding: the code changed, so look again.
- `.markEdge(src, dst:, label:, stroke:)` labels or strokes one graph edge.
- `.flowIn(actor, dst: dir)`, `.flowOut(dir, dst: actor)` and `.flowAcross(actor, dst:
  actor)` are the only hand edges. Each has an actor at one end and claims flow, which
  nothing checks. A hand edge where the graph already draws one is a finding.

A relationship between two packages that no import holds, a socket or an HTTP
call, is declared in the source with a `magus:calls <dir> <transport>` marker, never drawn
by hand. The marker becomes a calls edge `edgesFromGraph()` draws.

## Scope

`.scope(dirs)` names the directories the figure answers for. Every one must be drawn by a
box or a group, or left out with `.except(dirs, why:)`, which is for a true exclusion, not a
list of packages the picture did not fit. Narrow the scope instead. A figure with no code
behind it, a decision process or a CI arrangement, says so with `.unscoped(why:)`.

## Findings name the call to change

Nothing is drawn until the figure is clean. Real ones:

```text
figure "server-http": "internal/server" is in scope("internal/**") but nothing draws it; add it to a box or a group, or except([...], why:)
figure "t": draws 2 boxes of code and no edge between them; call edgesFromGraph()
figure "t": "a" has no symbol index, so its imports are unknown and edgesFromGraph() cannot draw them; build it with `magus graph build`
```

Do what the finding says, at the call it names. A finding ending `layout bug, report it`
is not yours to fix: reorder declarations or split the figure, and report it.

## Register and embed

A figure nobody registers is checked by nothing. Find the workspace's registry with
`magus query diagrams`; in magus's own tree it is `docs/site/diagrams/all.buzz`, one
import plus one entry. `magus run diagrams-generate docs` lays every figure out from the
symbol index and writes the light, dark and page SVGs with a JSON receipt beside each:
the stamp, the claim counts, the findings, and the index digest it was drawn at. A docs
page embeds a figure by id with `<!--diagram:<id>-->`.

Source code can point back at a figure with a `magus:diagram <id>` comment beside the
code a box depicts; a marker whose figure is gone is a finding.

## The console

The console's Diagrams page draws the workspace itself, with no figure file: the
project graph, one project's targets, and the package import graph, each through a
declared lens of scope, focus and depth. It needs a running server
(`magus server start`), and the import view needs the symbol index. The server
renders with the same module, so a lens over the budget is refused with its own
finding: narrow the lens rather than asking for a bigger picture.

## What this skill refuses

- **Coordinates, weights, pins.** A layout knob grows into a second language. Shape a
  figure with `rank`, `row`, `zone` and `boundary`, or split it.
- **Hand-drawn code edges and path strings.** An edge nobody's code holds asserts
  something nobody checked, and a path string goes stale the day a package moves.

Writing the Buzz itself, the syntax and the strict-mode rules: magus-buzz-lang.
generatedinternal/agent/skills/magus-diagram/SKILL.mdagentsskillsmagus-diagram
Last updated (895d94e4)
Earlier changes on this page (4)

Full history ↗ · Blame source ↗

Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. See workspace.

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Module

A magus stdlib namespace a magusfile imports for host capabilities: filesystem, exec, vcs, and more. See the module reference.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Server

The background process a person starts with magus server start. It serves MCP, the console, background jobs and the warm knowledge graph, and adopts nested magus calls into one pool. See server.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs: magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Knowledge graph

The queryable graph of a workspace's spells, targets, docs, and code relationships; query it with magus query/explain/path. See knowledge.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate.

Pane

A split within a tab. Splitting divides the focused pane along its longer side, so the same action tiles side-by-side on a desktop and stacks on a phone; a tab with no split is a single pane. Drag the divider to re-weight the split. See reference/console.

Conventions

Placeholders

Angle brackets mark a value you replace with your own; never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.