22 · Block diagrams for agents
This is the guide Ensemble gives an agent when a turn asks for a diagram. The same text is the skill at .github/skills/block-diagrams/SKILL.md. The assistant's diagrams playbook (apps/hub-api/src/assistant/skills/diagrams.md, 11 §10.1) appends this file after its own steps and examples; the playbook loads when the message is about a diagram, a diagram is mentioned or open, or the model asks for it with hub_how_to. It is not injected on unrelated turns. The packaged desktop app carries a copy beside its API.
The DSL itself, including curve and the fit presets, is in docs/21_BLOCK_DIAGRAMS_DSL.md.
Block diagrams
You draw diagrams by writing Ensemble diagram text and saving it with the diagram tools. The person edits on the canvas. You do not invent boxes.
The language reference is docs/21_BLOCK_DIAGRAMS_DSL.md. This skill is how to decide what to draw.
Tools
Reads:
hub_list_diagrams— diagrams this person already has.hub_get_diagram— one diagram’s text, so you can edit it.hub_validate_diagram— parse the text and return diagnostics. It does not save.hub_explain_diagram— a plain-language reading of one diagram. Use it when they ask what a diagram says, including someone who does not write software.hub_diagram_context— the bounded text of a task, project, deliverable, or page: title, notes, page text, deliverables, tasks, meeting notes, and linked repo ids. Call this before drawing from a task, project, deliverable, or page.hub_repo_overview— a compact summary of a repository. A saved link is enough. You do not clone, and you do not call GitHub yourself. The server uses an allowed checkout, a cached shallow mirror, or the GitHub API.hub_repo_read_file— one file from that same checkout or mirror, when a heading is ambiguous.
Writes (the person presses Apply; until then say “Ready to apply”, not “I created it”):
hub_create_diagram—title,text, and optionallinks(task,deliverable,project,repo, orpage, each with an id you already have).hub_edit_diagram— semantic edits that keep ids, locks, and layout:add_node,remove_node,rename_node,add_edge,remove_edge,move_to_group,set_node,set_curve,set_edge, or onepatchfind/replace. Prefer this when a diagram already exists.hub_update_diagram— replace the wholetext, or a single uniquefind/replace. Keep ids.
If a name is not in hub_diagram_context, a repo overview, a file you read, or the person’s message, it is not in the diagram.
When to draw
Draw when the person asks for a diagram, a flowchart, an architecture picture, or “show me how X fits together”.
Do not draw when they asked for a task, a deliverable, or a paragraph. Offer a diagram only if the structure is the point of the answer.
One request, one diagram, unless they asked for several or the picture will not fit in 20 blocks.
Which kind
Pick the kind from the facts, then name blocks after those facts.
| Kind | Use it when | Direction | Typical shapes |
|---|---|---|---|
| Flowchart | A person or job moves through steps and a question | down | circle start, rectangle step, diamond question, rounded screen |
| Architecture | Services and the stores they call | right | server, cylinder, cloud, rounded app |
| Sequence-style | A few parties and the messages between them, in order | right | actor or rectangle per party; edge labels are the messages |
| Data pipeline | Records move from a source, through jobs, into a store | right | parallelogram input, server job, cylinder store |
| ER-ish | A handful of records and how they point at each other | right | rectangle entity, edge label is the relationship |
| Org | People and who they report to | down | actor |
| Decision tree | One question splits into outcomes | down | diamond, then a short chain per answer |
| Repo or module map | The parts of one repository the workspace actually names | right | rectangle module, group per area |
Sequence-style is still boxes and arrows. Do not invent a lifeline syntax. Put the parties in a row and label each arrow with the message.
An ER-ish diagram names entities and relationships. It is not a full schema. Skip columns unless the person listed them.
Gather facts first
- If they named a task, project, deliverable, or page, call
hub_diagram_contextwith that id. Use the title, description, notes, page text, deliverables, tasks, and meeting notes it returns. Link those ids. - For each repo id in that payload, or when they named a repo (
@a repository, or “diagram of <owner/name>”), callhub_list_reposif you still need the id, thenhub_repo_overviewonce. Draw only apps, services, stores, and files the overview names. Read one or two files withhub_repo_read_filewhen a heading is ambiguous. Link the repo id. Do not clone the repo and do not browse GitHub yourself. - If they pasted a tree, a manifest, routes, or a schema, use only the names in that paste.
- Read an existing diagram with
hub_get_diagrambefore you change it. Refine it withhub_edit_diagram. - Call
hub_explain_diagramwhen they want the picture described in sentences.
Every block label must appear in that material or in the person’s own words. A box called “Auth service” is allowed only if something you read says so. Prefer fewer true boxes over a plausible architecture.
A well-known system the person names, rather than their own work (the transformer architecture, the OAuth 2.0 authorization code flow, a TCP handshake), is drawn from its standard, textbook design with its standard names. Do not add parts of their own system to it unless they gave them.
If the material is too thin (a repo name and nothing else), draw that one box and say what you could not see. Do not fill the gaps.
Names, ids, labels
- Quote every label:
node api "API" shape server. - The id is short, stable, and lowercase:
api,users-db,valid. - The label is what a person reads. It can have spaces.
- Refer to ids on edges, not to the label text.
- Never reuse an id. Never rename an id on an edit unless the person asked.
- Do not use ids
straight,curved,elbow,curve,shape, orcolor.
Size
Aim for 7 to 20 blocks. Under 7 is fine when the facts are few. Over 20, split into two diagrams or put a cluster in a group.
A group is a dashed frame with a label, not a new system. Example: a group edge around the web and mobile apps, a group data around the databases.
Direction and curves
direction downfor flows, orgs, and decision trees. Fit vertically in the editor does the same.direction rightfor architecture, pipelines, ER-ish maps, and repo maps. Fit horizontally does the same.- Leave
layoutout of a new diagram. The editor places the blocks when it opens. Alayoutthat puts blocks or group frames on top of each other is dropped byhub_create_diagramandhub_update_diagram(locked positions stay), and the result sayslayoutDropped. curvedefaults tostraight. Setcurve elbowfor architecture and ER-ish maps (right-angle connectors). Setcurve curvedonly when a flow should feel soft. One diagram-level line is enough.- Add
curveon a single edge only when that edge should differ:edge api > cache curve curved : "optional".
Shapes
Leave color off. The shape already has a colour.
| Shape | Means |
|---|---|
| rectangle | a step, module, or service with no stronger shape |
| rounded | a screen |
| diamond | a decision |
| circle | start or end |
| cylinder | a database or store |
| server | a service or machine |
| parallelogram | input or output data |
| document | a file or report |
| cloud | something outside this system |
| actor | a person |
| hexagon | a check or a gate |
| note | a note stuck on the diagram |
| trapezoid | a manual step |
| queue | a message queue or event stream (Kafka, SQS, a topic) |
| subroutine | a step defined elsewhere (a predefined process) |
| browser | a web app or website |
| delay | a wait |
text is a caption, not a block. It cannot take an arrow.
Colour overrides are rare. color red on an error step is enough. Do not paint every block.
Edges
edge a > bis a solid arrow.edge a --> bis dashed.edge a -- bhas no arrowhead. Use it for a lookup or a relationship that is not a step.edge a > b : "yes"labels the line. Keep labels to one or two words.- Two blocks have one line between them. A second line between the same pair (either way round) is merged into the first, so write a request and its reply as one two-way line:
edge app <> api : "calls / answers". Do not draw both directions unless both are in the source. - A chain
edge a > b > cis two edges. Prefer separate lines so each can have a label.
Self-check
Before hub_create_diagram or hub_update_diagram:
- Call
hub_validate_diagramwith the full text. - If
errorsis not 0, fix the lines it names and validate again. - If
warningsis not 0, fix those too. A warning means a block was invented or a word was skipped. - Save only when
okis true. - On create, pass
linksfor every id the person pointed at.
Do not call the write twice while it is waiting for Apply.
Editing an existing diagram
hub_list_diagramsif you do not have the id, thenhub_get_diagram.- Prefer
hub_edit_diagram. A locked block cannot be removed or moved. Rename and colour changes keep the lock. Ids and layout lines stay. add_nodeneedslabeland usuallyshape.add_edgeneedsfromandto; on two blocks already joined it changes that line (its label, its curve) instead of adding one.rename_nodeneedsidandlabel.set_nodesetsshapeorcolor.set_curvesets the diagram curve, or one edge whenedgeis set.set_edgechanges one line, byidor byfromandto:label,curve,dashed,arrows(end,start,both,none),width(px) andhead(arrowhead px).patchis one uniquefind/replace.- Text size and emphasis (
font 16,bold,italic,underline), line width (width 2), arrowhead size (head 14) and bends (bend 30) are written in the text after the other properties. Use them only when the person asks; a new diagram needs none of them. - The Apply card shows the added, removed, and changed blocks. Do not describe a change you did not send.
- Validate, then the write. A full
hub_update_diagramtext replace must still contain the layout and locks you did not mean to drop.
Common mistakes
- Boxes that were not in the repo, project, task, or message.
- A diagram of a whole company when the task is one route.
- More than 20 blocks in one picture.
- Colours on every node.
curveon every edge when the diagram already sets it.- New ids for blocks that already exist.
- Rewriting
layoutand clearing locks. - Saying the diagram exists before Apply.
- A second diagram when they asked to edit the one in the list.
Worked examples
A task that names three steps
Facts from hub_get_task: title “Login”, description “User submits the form. If the password matches Users, open the dashboard. Otherwise show an error.”
title Login
direction down
node start "Start" shape circle
node form "Submit form" shape parallelogram
node check "Password matches?" shape diamond
node home "Dashboard" shape rounded
node err "Show error" shape rounded
node users "Users" shape cylinder
edge start > form
edge form > check
edge check > home : "yes"
edge check > err : "no"
edge form -- users : "lookup"
Link task to that task id. Users is in the description, so the cylinder is fair. Do not add “session service”.
A task, from hub_diagram_context
hub_diagram_context for the task returns a description “The clinic web app calls the API. The API stores visits in Postgres.” and a project named “Clinic visits”.
title Clinic visits
direction right
curve elbow
node app "Clinic web app" shape rounded
node api "API" shape server
node db "Postgres" shape cylinder
edge app > api
edge api > db
Link task to that task id. Do not add a box the context did not name.
This monorepo, from hub_repo_overview
hub_repo_overview for ensemblework/ensemble names apps hub-web, hub-api, agent-runtime, context-bridge, and skill-forge. Compose services are postgres and redis. A notable file is apps/hub-web/components/shell/quick-capture.tsx. skill-forge is drawn because the overview names it, and it gets no arrow because nothing read says what it calls.
title ensemblework/ensemble
direction right
curve elbow
node web "hub-web" shape rounded
node api "hub-api" shape server
node agent "agent-runtime" shape server
node bridge "context-bridge" shape cloud
node forge "skill-forge" shape rectangle
node capture "quick-capture" shape rectangle
node db "Postgres" shape cylinder
node cache "Redis" shape cylinder
edge web > api
edge agent > api
edge bridge > api : "read"
edge web > capture
edge api > db
edge api > cache
Link repo to that id. Postgres and Redis are the compose services. quick-capture is a file under hub-web, so it hangs off the web app. Do not add a queue, an auth service, or a second database.
A repo description that names parts
Description: “Next.js app. API routes in app/api. Prisma schema for Account and Invoice. Stripe webhooks.”
title acme/billing
direction right
curve elbow
node web "Next.js app" shape rounded
node api "API routes" shape server
node db "Account and Invoice" shape cylinder
node stripe "Stripe" shape cloud
edge web > api
edge api > db
edge stripe > api : "webhook"
Those four names are in the description. Do not add Redis, a queue, or an admin app.
Architecture with a group
Project summary: “Clinic web app and the worker share one API. The API stores visits in Postgres.”
title Clinic
direction right
curve elbow
group clients "Clients" {
node web "Web app" shape rounded
node worker "Worker" shape server
}
node api "API" shape server
node visits "Visits" shape cylinder
edge web > api
edge worker > api
edge api > visits
Link the project id.
A pipeline from a pasted note
The person pasted: “Nightly job reads the CSV export, writes rows to the warehouse, then the report job builds the PDF.”
title Nightly import
direction right
node csv "CSV export" shape parallelogram
node load "Nightly job" shape server
node warehouse "Warehouse" shape cylinder
node report "Report job" shape server
node pdf "PDF" shape document
edge csv > load
edge load > warehouse
edge warehouse > report
edge report > pdf
A decision, kept small
Task: “If the PR has tests, merge. If not, request changes.”
title Review
direction down
node pr "Pull request" shape document
node tests "Has tests?" shape diamond
node merge "Merge" shape rectangle
node changes "Request changes" shape trapezoid
edge pr > tests
edge tests > merge : "yes"
edge tests > changes : "no"
Editing without moving locked blocks
Existing text includes node desk "Desk" shape rectangle locked and a layout section that places desk. The person says “rename Desk to Front desk”.
Prefer hub_edit_diagram with one op: { "op": "rename_node", "id": "desk", "label": "Front desk" }. The id stays desk. The lock and the layout line stay. Validate if you are unsure, then the write. Do not send a full text that omits layout.
Adding a block without dropping locks
The diagram already has locked node api "API" shape server. The person says “add Redis, the API uses it.”
hub_edit_diagram ops:
{ "op": "add_node", "id": "cache", "label": "Redis", "shape": "cylinder" }{ "op": "add_edge", "from": "api", "to": "cache", "label": "uses" }
The Apply preview lists the added block and the added arrow. api stays locked.
Two diagrams instead of one crowded map
A pasted tree has 30 packages. Draw the top-level folders as one diagram (one block per folder, grouped if the paste groups them). Offer a second diagram only for the folder they ask about. Do not draw all 30 packages in one picture.