Produce a valid, readable native .drawio file. Native XML is the source of truth and remains beside any export. This skill exists for draw.io compatibility and editability; a generic diagram, geometry, grid, or chart belongs to technical-figure.
Quick path
-
Form the reader brief in the content contract below. Fix the reader and the one question; do not reduce the material to a sentence before you know what the peer must be able to verify.
-
Derive the required nodes, edges, groups, and annotations from that brief. Every component and relation the peer needs to verify the answer is required; a fact that answers a different question stays outside the figure. Read
references/local/figure-principles.mdandreferences/local/figure-style.mdunless the user supplied a different style or an existing file already establishes one. -
Read
references/local/upstream-drawio-rules.md. Start with uncompressed baremxGraphModelXML unless pages or file metadata require<mxfile>. -
Choose a layout that preserves semantic grouping and the intended reading path. Automatic layout is the default for a new graph; use explicit placement when geometry or an existing design carries meaning.
references/local/auto-layout.mddescribes the bundled route. -
Run both validators from the skill root:
python3 scripts/validate_drawio_xml.py <file>.drawio python3 scripts/validate_drawio_layout.py <file>.drawio -
For every new diagram or substantial visual edit, export and inspect SVG or PNG when an exporter is available, in the dark theme as well when readers will see it dark. Apply
references/local/review-loop.md: fix render defects first, then remove any text the document should carry. If a route is ambiguous or collides with content, usereferences/local/edge-routing.md, fix the affected routes, and validate again.
Reader and content contract
Form this working brief before authoring. It is the comparison set for the finished file, not a questionnaire: infer it from the request and material, ask only when a missing relation or conflicting fact would change the answer, and continue the supported parts meanwhile.
reader: <default: a peer in this domain who lacks only this project's context>
question: <the one question this figure answers>
answer: <what the peer should be able to verify from the figure>
surrounding_context: <what the document or conversation already explains>
required_nodes: [exact names; a role on a second line only where the name does not say what it does]
required_edges: [{from, to, what passes or what triggers it}]
required_groups: [real boundaries only]
required_annotations: [{text, purpose}] # empty is normal; allowed only when meaningful: gray annotation-layer notes, a short legend, a zoom panel, a locator; never a title, caption, footer, badge, or icon
deferred_to_prose: [supporting context and provenance; keep quantities and conditions needed to interpret the answer on the canvas]
The reader is a peer: knowledge is not their bottleneck, this project's structure is. Assume the field's vocabulary and supply the exact names, mechanisms, and relationships they could not reconstruct without the figure. A deliberately simplified figure is a separate register that only an explicit request selects.
Three content rules do most of the work; references/local/figure-principles.md holds the full set.
- Exact names first. Label a component by the name it carries in code, configuration, or the running system; add a role on a second line only when the name does not say what it does. Never a role in place of a name. Paths, endpoints, fields, and hook points are content when the peer needs them to verify or act on the answer.
- Edges carry mechanism. Only semantic nodes receive connectors. Label a connector with what passes, what triggers it, or its condition whenever the two node names leave the relation ambiguous, and keep the label to the mechanism.
- Preserve the answer and its conditions. Keep numbers, dates, sample sizes, and qualifiers when they encode the mechanism, distinguish compared cases, or prevent a false reading. Put supporting context and provenance beside the figure. Thresholds, timeouts, dimensions, repetition counts, and state labels may stay.
For a scoring or aggregation figure, read the scored unit and its roll-up into the reported metric from the source before drawing, then show that roll-up. Two conditions judged on one item are still one item, not two.
When the page is hard to scan: organize (group by real boundary, align peers, order along the reading path), then split into a series of pages at the same depth, then move to prose only what answers a different question. Do not abstract nodes into roles, merge components that differ in a relevant way, or delete mechanism to fit. Never reduce type size to preserve an overfull composition.
Native XML baseline
- Include
mxGraphModelroot cells0and1; useadaptiveColors="auto"and bake the background withbackground="#FFFFFF". - Use uncompressed XML and stable unique IDs. Do not emit XML comments.
- Emit vertices before edges. Every edge has source and target IDs plus a child
<mxGeometry relative="1" as="geometry" />. - Use
html=1;and XML-escape label HTML. A literal\nis not a line break; use<br>or
. - Use
swimlaneorcontainer=1only for real grouping. Use<object>orUserObjectonly when metadata improves editability.
Read references/local/text-and-labels.md only when labels need multiline HTML, edge-label positioning, metadata, or another nontrivial treatment.
Layout decision
For a bounded edit to an existing file, retain cell IDs, pages, metadata, and unaffected geometry. Apply layout only to the changed region when needed; rebuilding the whole diagram is not a prerequisite for correcting one label or connection.
Automatic layout is the default for new files whose topology fits it:
- a linear process:
horizontalFloworverticalFlow; - a hierarchy:
horizontalTreeorverticalTree; - nested or routed architecture: explicit ELK JSON, optionally followed by
orthogonalEdge.
Automatic layout is an explicit authoring step, not a viewer cleanup pass. The saved file must already contain an acceptable layout. Use explicit coordinates, fixed ports, or waypoints when they preserve a meaningful layout or resolve obstacles, parallel paths, or return corridors. A failed automatic-layout attempt is not a prerequisite for these choices. Never add invisible spacer nodes or supporting bands to manipulate geometry.
Verification
XML validation is blocking. Treat layout warnings as evidence to inspect and fix; accept one only when the rendered source is still clear and the tradeoff is reported. Validators do not detect every clipped label or visual collision.
Before finishing, confirm:
- every label is readable at the intended delivery size and no component, label, or unrelated edge overlaps; the render is what the source intends;
- the required semantic inventory matches the diagram, exact names are present, no relationship was invented, and numbers and qualifiers needed to interpret the answer remain visible, while supporting context sits beside the figure;
- the peer can verify the answer from the figure; a page that says no more than its heading is not finished;
- the focal element or path reads first, context stays neutral or ghosted, and each line style carries one relation;
- every visible element earns its place; and
- a render was inspected for every new diagram or substantial visual edit, or the missing exporter was reported.
Deliver after these checks pass. Revalidate for a relevant edit or defect; do not add formats or redraw unaffected pages to extend verification.
Exports
Use an existing draw.io CLI; do not install one for a source-only request. Find it in the order scripts/apply_auto_layout.py uses: $DRAWIO_BIN when set, then drawio on PATH, then the macOS app binary, which is not on PATH by default:
DRAWIO="${DRAWIO_BIN:-$(command -v drawio || echo /Applications/draw.io.app/Contents/MacOS/draw.io)}"
"$DRAWIO" -x -f svg -e -b 16 -o <name>.drawio.svg <name>.drawio
"$DRAWIO" -x -f png -e -b 16 -s 2 --theme light -o <name>.drawio.png <name>.drawio
"$DRAWIO" -x -f png -e -b 16 -s 2 --theme dark -o <name>.dark.drawio.png <name>.drawio
Prefer SVG for sharp text. The SVG keeps draw.io's default --theme auto and follows the viewer's color scheme; PNG and PDF render one theme. Deliver the theme that matches the host, light when nothing in the context says otherwise, and read references/local/figure-style.md for what draw.io's automatic dark palette keeps and loses. If the exporter is unavailable, deliver the valid .drawio source and report that export and visual inspection were unavailable.
Output
Lead with the artifact created or changed. Report both validator results, the layout route used, and the exact export inspected. Keep <name>.drawio beside <name>.drawio.svg, <name>.drawio.png, or <name>.drawio.pdf. Name any accepted warning or unverified rendering.
Reference router
| Need | Read |
|---|---|
| Content and shared appearance invariants | references/local/figure-principles.md |
| draw.io recipes for the figure tokens, dark rendering, and backend limits | references/local/figure-style.md |
| Required XML structure and export rules | references/local/upstream-drawio-rules.md |
| Automatic layout and current CLI routes | references/local/auto-layout.md |
| Reader, density, and rendered visual audit | references/local/review-loop.md |
| Explicit ports, waypoints, and crossings | references/local/edge-routing.md |
| Multiline, HTML, metadata, or edge labels | references/local/text-and-labels.md |
| Deep official syntax lookup | references/local/upstream-docs-map.md |
Read only the rows needed for the current artifact. Vendored files under references/fetched/ are factual lookup material, not workflow instructions.
Gotchas
- Applying
--layoutis an explicit authoring step; reopening a saved file does not perform another obstacle-aware cleanup. - File each edge at the innermost container that holds both endpoints, and on the layer (
parent="1") only when an endpoint is outside every container. Auto-layout reads an edge in its parent's frame, so an edge filed further out than its endpoints is laid out in the wrong place. - Empty space may stay empty; it does not need a title, footer, rail, icon, or decoration.
- Three draw.io style traps: with
absoluteArcSize=1,arcSizeis the corner diameter;dashPatternscales withstrokeWidthunlessfixDash=1is set; andfontStyle=1or<b>renders weight 700, so labels take 600 from an HTML span. - A crowded inventory is not a clear explanation, and a chain of role-named boxes that restates its heading is not a figure. Fix crowding by organizing and splitting into pages, not by turning names into roles or deleting mechanism.
- Text above or below the figure (a title, a takeaway, an analogy, a source line) is the document's job and a defect on the canvas. The gray annotation layer holds only names and mechanism.
apply_auto_layout.pyshrinkscontainer=1groups while their children keep their size, so a file that passed the layout validator before layout fails containment after it. Restore the child geometry with real padding and aligned stage centers instead of keeping the laid-out boxes.