agentleFS
aliefauzanaliefauzan/Orthogram/skills/drawing-flowcharts/SKILL.md

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

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
  1. Very specific:

    Use the folder the user names.

    line 27
  2. Very specific:

    Never leave a build script, a .drawio or a preview in the project root, and never write outside the project you were pointed at.

    line 34
  3. Distinctive:

    A legend: when you use more than one connector colour.

    line 49
  4. Very specific:

    Every path ends in a terminal.

    line 63
  5. Distinctive:

    Waypoints: Every route leaves and arrives straight, along the normal of the face it uses.

    line 151
  6. Distinctive:

    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 …

    line 229

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 at

Use 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:

  1. 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.
  2. check_routing.py reporting 0 FAIL.
  3. Both arms of every decision labelled — say what makes it true, not just "Yes". branch() requires both.
  4. A legend when you use more than one connector colour.

The shapes

start / terminalblue pill — where the flow begins and each way it ends
processwhite rounded box — a step someone does
decisionrhombus — a question with exactly two answers
ioparallelogram — data going in or coming out
subflowbox with side bars — a step that is its own flowchart
storecylinder — something written down or read back
notegrey 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.

node220 × 60, taller when the label needs it (decision 220 × 80)
row pitch130 — room for a label on every connector
column pitch420 (node 220, gutter 200, 6 corridors)
corridor pitch26, 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 node

Connectors

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 look

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 in it, a label that says "process" instead of what happens.

Common mistakes

Unlabelled branchesThe reader cannot tell which arm is which. Both, always.
One end for every pathName each ending, including the failures.
A decision with three armsThat is two decisions.
Looping back with then()Refused — a return path needs its own corridor to stay visible.
Steps named as nounsA step is something done: "Issue the refund", not "Refund".
Reporting done without the checkerThe routing failure is invisible until it renders.
Two arrows on one face of a boxSpread 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 poolsThe reader has to trace connectors to work out who acts. One pool per actor.
A pool per column and a lane per rowBands 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.