drawing-flowcharts
Skill2 starsChanged 2 months agoMITView on GitHub
Use when drawing a flowchart, process flow, decision tree, swimlane diagram, runbook, approval flow, state or sequence of steps as a .drawio / draw.io file, or when an existing flowchart's connectors cross boxes or its branches are unlabelled
In this repository5 other files it gives its agents.
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 27
Use the folder the user names.
- Very specific: line 34
Never leave a build script, a .drawio or a preview in the project root, and never write outside the project you were pointed at.
- Distinctive: line 49
A legend: when you use more than one connector colour.
- Very specific: line 63
Every path ends in a terminal.
- Distinctive: line 151
Waypoints: Every route leaves and arrives straight, along the normal of the face it uses.
- Distinctive: line 229
Read the picture for what a checker cannot see: a decision whose two arms mean the same thing, a path with no ending, a lane with one step …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Drawing flowcharts
A .drawio flowchart in the same visual language as the architecture diagrams:
light page, white shapes with hairline borders, tinted lane bands, Google's
palette on the connectors, and every connector on a route you chose.
Build it with flow_drawio.py (this directory). Check it with
check_routing.py. Look at it with preview.py.
Where the files go
Everything this skill produces lives in one folder, never loose in the project root:
docs/diagrams/
refund-flow.py # the script that builds it
refund-flow.drawio # the diagram
refund-flow.preview.svg # what you looked atUse the folder the user names. Otherwise put it under docs/diagrams/ when the
project has a docs/ directory, and diagrams/ at the project root when it
does not — the build script, the .drawio, and any preview or PNG together.
write() creates the folder if it is missing, so
f.write("docs/diagrams/refund-flow.drawio") is all it takes, and preview.py
writes the SVG beside its input.
Never leave a build script, a .drawio or a preview in the project root, and
never write outside the project you were pointed at. One folder, named files —
a diagram whose script sits next to it can be regenerated; one scattered across
a repository root cannot be found again.
The output contract
The file you hand back has all four of these:
- Every connector routed.
then()for the straight drop to the next step,to()with a corridor for everything else. Neither will build an unrouted connector. check_routing.pyreporting 0 FAIL.- Both arms of every decision labelled — say what makes it true, not just
"Yes".
branch()requires both. - A legend when you use more than one connector colour.
The shapes
start / terminal | blue pill — where the flow begins and each way it ends |
process | white rounded box — a step someone does |
decision | rhombus — a question with exactly two answers |
io | parallelogram — data going in or coming out |
subflow | box with side bars — a step that is its own flowchart |
store | cylinder — something written down or read back |
note | grey band — commentary, not part of the flow |
Every path ends in a terminal. A flowchart with one start and one end that
silently drops three branches on the floor is the commonest defect in the form;
name each ending, including the unhappy ones.
Rows are the reading order
col and row place a node. Rows run down the page in the order things
happen; columns are for branches and rejoins.
f = Flow("Refund request", "support · payments · ledger")
s = f.start("Refund requested", col=0, row=0)
age = f.decision("Older than 30 days?", col=0, row=2)
deny= f.terminal("Decline · explain why", col=1, row=2)
f.then(s, look)
f.branch(age, no=paid, yes=deny, no_label="No · within window", yes_label="Yes")branch() works out the geometry: straight down when the arm is in the same
column, straight across when it is beside, a corridor when it is neither. Green
for the arm that continues, red for the one that stops or diverts.
| node | 220 × 60, taller when the label needs it (decision 220 × 80) |
| row pitch | 130 — room for a label on every connector |
| column pitch | 420 (node 220, gutter 200, 6 corridors) |
| corridor pitch | 26, first at +30 |
Constants are a starting point. Count the return paths — a retry, a decline, a
rejoin — before you place nodes, and widen COL_PITCH if a gutter needs more
than six:
import flow_drawio as F
F.COL_PITCH = 560 # before creating any nodeConnectors
then(src, dst) is the straight drop to the step directly beneath. It checks
that the two are in the same column, that the target is below, and that nothing
sits between — so it cannot quietly draw through a box.
to(src, dst, label, colour, exit=, entry=, corridor=) is everything else: a
loop back to an earlier step, a jump across columns, a side effect. Pass
corridor=f.corridor(after_col=N) for its own vertical lane, or points= for
a route no single corridor can express. Every hop must be horizontal or
vertical.
Going back up the page is normal in a flowchart — a retry, a rework loop — but
it must use a corridor so the return path is visible rather than overlapping
the forward one. then() refuses to go upward for that reason.
Where two connectors meet
Two edges that both leave the middle of a box's face leave the same pixel, and their runs lie on top of each other out to the corridor — the commonest reason a flowchart is unreadable with every connector correctly routed. The first edge on a face keeps the middle and the rest are spread along it, automatically; an anchor you pass yourself is never moved.
Where one connector genuinely crosses another, the horizontal one hops over it
— jumpStyle=arc in the file, an arc in the preview. Without the hop a
crossing and a junction are the same picture.
A connector's label sits in a break in that connector's own line, on an opaque plate, and every plate is drawn after every path — a label floating in the gap between two parallel runs belongs to neither of them. When the middle of the line is taken, the label slides along it, and steps aside by one plate width — no further, or it stops belonging to anything — when staying on would cover a box, cover another label, or swallow a hop too short to hold it. Two labels never land on the same spot.
Neither trick saves a route that is wrong. check_routing.py still reports two
runs sitting on the same line, and that warning means the two want different
corridors or different anchors, not a bigger arc.
What is settled at write time
Three things are worked out when the file is written, not when each connector was added, because each depends on every other connector — and half of those did not exist yet at the time:
- Anchors. Two connectors that both leave the middle of a face leave the
same pixel and run on top of each other. Anchors left at the middle are
spread; a fraction you pinned yourself is never moved, and the spread avoids
it. (Ask for
R(0.2)and nothing else lands within about 8px of it.) - Waypoints. Every route leaves and arrives straight, along the normal of the face it uses. Without that the last hop can run along a box's edge and the arrowhead points sideways into a corner — the defect reads as a bend at the tip. Corners that are not corners are dropped.
- Label positions. Each label is placed clear of other connectors, of crossings, of boxes and of the other labels, as near the middle of its own line as those allow — then pinned into the file, so draw.io and the preview show the same picture.
Corridors and bands start beyond the approach line, so a lane never runs alongside the straight bit of a connector leaving a face: two lines 4px apart read as one thick line with the labels floating between them.
Colour means kind of path
FLOW blue for the main path, YES_C green for a condition holding, NO_C
red for one failing, ALT yellow for an alternative or retry, NOTE grey
dashed for a side effect that is not part of the flow.
Bands: pools down, lanes across
A flowchart with more than one column is nearly always a flowchart with more than one actor, and the frame should say so — otherwise the reader works out who does what by following connectors, which is the work the picture was supposed to save.
f.pool("Connected agent", cols=[2]) draws a band down those columns: the same
solid panel the architecture diagrams use — #E1F5FE, no border, no shadow,
the name in grey at the top left. White cards read against it. Declare one per
actor, left to right.
f.pool("Human", cols=[0])
f.pool("The page", cols=[1])
f.pool("Connected agent", cols=[2])f.lane("Payments service", rows=[3, 5]) is the other axis: a translucent band
across a range of rows, for a phase rather than an actor. Pools and lanes can
be used together — a lane reads as a stripe over the pools — but only when the
grid really is actor × phase. One band per column and one per row on the same
picture is noise, not structure.
One tint for every pool, the way every panel on an architecture diagram shares
one tint. Colour on a flowchart belongs to the connectors, where it already
means something: a wash per pool turns three meanings into six and the page
reads as decoration. tint= is there for the one band that must differ — reach
for it once, not once per pool.
A lane crossing pools lightens them rather than greying them, and its name sits on the right, because the top left of a band is the pool's.
Pool bands sit 22px outside their columns, inside the first corridor of the
gutter, so a return path runs between two pools rather than along the edge of
one. If you widen COL_PITCH, the bands follow.
The page sizes itself
Flow(...) takes no size and measures the page from what is on it at write
time. width=/height= still work and are read as a floor, so a page never
clips its own content and never prints as a field of grey around it.
Boxes grow to their labels
A step whose name needs two lines gets a taller box, centred on the same row
line, so nothing moves off the grid and no label spills out of its shape.
Boxes carry whiteSpace=wrap, so draw.io wraps the same way the preview does.
Three lines in a box is a sign the step is really two steps.
Before handing it over
S=~/.claude/skills/drawing-flowcharts
python3 "$S/check_routing.py" docs/diagrams/out.drawio # must report 0 FAIL
python3 "$S/preview.py" docs/diagrams/out.drawio # writes docs/diagrams/out.preview.svg
rsvg-convert -w 1400 -o docs/diagrams/out.png docs/diagrams/out.preview.svg # now you can actually lookRead the picture for what a checker cannot see: a decision whose two arms mean the same thing, a path with no ending, a lane with one step in it, a label that says "process" instead of what happens.
Common mistakes
| Unlabelled branches | The reader cannot tell which arm is which. Both, always. |
| One end for every path | Name each ending, including the failures. |
| A decision with three arms | That is two decisions. |
Looping back with then() | Refused — a return path needs its own corridor to stay visible. |
| Steps named as nouns | A step is something done: "Issue the refund", not "Refund". |
| Reporting done without the checker | The routing failure is invisible until it renders. |
| Two arrows on one face of a box | Spread automatically, but an arrow into a decision's side vertex and its branch out of the same vertex is still yours to fix. |
| Columns that mean actors, with no pools | The reader has to trace connectors to work out who acts. One pool per actor. |
| A pool per column and a lane per row | Bands on both axes only when the grid is really actor × phase. |
preview.py writes a plain SVG — open it in any browser if you have no rsvg-convert. The PNG step is for pasting into a document, not for looking at it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

