name: arc-diagramming-obsidian description: Use when the user wants an Excalidraw diagram or any visual representation — architecture, flowchart, mind map — including casual "draw this" / "show me how this works visually". Also use when arc-maintaining-obsidian delegates Synthesis visuals beyond embedded Mermaid.
arc-diagramming-obsidian
Diagrams should ARGUE, not DISPLAY. A diagram is a visual argument — structure mirrors concept, shape carries meaning. If removing all text leaves a meaningless grid of boxes, the diagram has failed.
Pipeline
DESIGN → BUILD → VALIDATE → SAVE
You own the full workflow. For complex diagrams, delegate mechanical phases (Build, Validate, Save) to subagents that read from agents/ — this keeps your context clean. For simple diagrams or when subagents aren't available, execute each phase yourself. The instructions below are self-contained either way.
The rest of this skill is split into two layers of guidance:
- HARD — physical and mechanical invariants. The tools can verify these, or violating them silently corrupts the output. Non-negotiable.
- SOFT — concept judgments. Depend on what you're drawing, not on what the tools require. Reasoning, not rules.
Both layers apply across all four phases.
HARD: Invariants That Must Hold
Process Invariants
Detect theme before building. Choosing the wrong palette silently produces an unreadable diagram (dark palette on light background = pale washout; light palette on dark = near-invisible text). The Playwright renderer happily renders whatever colors you picked — no feedback loop to catch this.
obsidian eval code="document.body.classList.contains('theme-dark') ? 'dark' : 'light'"If no response within 5 seconds, pick a palette from prompt signals ("dark mode" in the request, time of day) and state the assumption in your completion output. Never silently assume.
Every Validate iteration must render AND view PNG. You cannot judge composition, readability, or hierarchy from JSON alone. The overlap checker catches bounding-box overlaps but not visual problems — arrows crossing unrelated elements at acute angles, text too small to read, hierarchy failing because the hero doesn't dominate. View
/tmp/diagram.pngwith the Read tool every iteration.Save must be verified. Both
ea.create()and the manual-fallback write path can silently produce format corruption — the canvas renders, markdown text bleeds through, and the Playwright renderer cannot detect this. Always runverify_saved_diagram.pyafter save.
Mechanical Invariants
ea.reset()at the start of every EA invocation — without it, elements accumulate from previous calls and appear in the output invisibly- Never change an element's
id— this orphans connected arrows (they lose the shape they were bound to) addTextwithboxreturns the BOX id, not the text id — use the returned value forconnectObjectsviewBackgroundColormatches the detected theme —#1e1e1efor dark,#fffffffor light- Save format is byte-exact for the manual fallback path — any deviation (wrong frontmatter spacing, list-style tags instead of inline array, missing warning line) causes silent corruption. See
references/save-format.md
Layout Trap Audit (Physical Collisions)
Four arrow-path collisions that recur across diagrams. Each is a rendered-pixel overlap, detectable by check_overlaps.py after build, but cheaper to prevent at design time by tracing arrow paths mentally.
- Trap 1: Converging arrows (multiple arrows merging into one target) travel through the corridor below the source elements. Annotations placed directly below source boxes get crossed.
- Trap 2: Back-edges (an arrow from a later zone returning to an earlier one) routed horizontally at the Y-midpoint of a zone they pass through will cross that zone's elements.
- Trap 3: Decision-diamond "yes" / "no" labels placed at the bottom of the diamond sit directly in the fan-out exit path of any arrow leaving from the bottom.
- Trap 4: Back-edge labels (free text describing the arrow) placed at the arrow's own vertical-run X-coordinate sit on top of the arrow line.
Mentally trace every planned arrow from source to target before writing EA code. If the path crosses an unrelated element, reposition before building. For fix strategies (arrow routing, anchor distribution, waypoint planning), read references/layout-heuristics.md Part 2.
SOFT: The Design Space
HARD keeps the diagram technically valid. SOFT is where you make it good. These are concept judgments — they depend on what you're drawing.
Think First, Draw Second
For each major concept in the diagram, answer before reaching for shapes:
- What does it DO? (the verb — not the noun)
- What relationships exist with other elements?
- What's the core transformation (input → output, state A → state B)?
- What would someone need to SEE to understand this?
Reasoning About What to Add
Every element should serve the concept. Before adding anything, ask: what does this communicate that labels alone don't?
If the answer is "nothing" — the element is noise. If the answer is "this is where the flow starts" / "this is state the user should sense" / "this is a zone boundary" / "this shows the concept is symmetric" — the element is doing work.
Two registers to match:
- Language register. If the prompt is monolingual, keep labels in that language. Don't add bilingual subtitles unless asked.
- Conceptual register. If the concept is symmetric (three equal stages, parallel peers,
Input → Process → Output), give peers the same size. Hero sizing implies real importance — don't fabricate one where the concept doesn't claim it.
Decoration is not the enemy. Unjustified decoration is.
Scale Reflects Real Importance
When one element is genuinely more important than peers (a convergence point that everything flows into, the hero concept the diagram is about), size it larger. When peers are genuinely equal, size them equally. Concrete size ranges are in references/painters-toolkit.md — they're suggestions for reference, not mandated tiers.
Isomorphism Self-Check (Heuristic)
Before building, ask: if I removed all text from this design, would the structure communicate anything about the concept? A fan-out says "one source, many outputs" without any labels. A convergence says "many inputs, one result." A cycle says "feedback."
If your planned structure is just "labeled boxes connected by arrows" with no isomorphism to the concept — the structure isn't doing work. Revisit.
This is a check, not a gate. Sometimes "A connects to B" is genuinely what the concept says, and that's fine. But the check prompts verification before committing.
The Painter's Toolkit
Shape variety, subtitles, zone labels, decorative accents, separators, containers, footer annotations — these are your brushes. Read references/painters-toolkit.md during design to see the full vocabulary, then pick what serves the current concept.
Not a menu to copy from. A vocabulary to pick from.
Visual Patterns (Reference)
For pattern inspiration — fan-out, convergence, tree, timeline, spiral/cycle, cloud, assembly line, side-by-side, gap — see references/visual-patterns.md. Each pattern has ASCII sketches, shape-meaning tables, and "when NOT to use."
Each major concept in a diagram typically uses a different pattern. If two adjacent sections look structurally identical, consider redesigning one — visual monotony kills comprehension.
Depth and Layout Planning
- For comprehensive/technical depth (research mandate, multi-zoom architecture, evidence artifacts), see
references/depth-enhancements.md. - For diagrams with 20+ elements or evidence artifacts, use
references/plan_layout.pyto compute coordinates automatically — it enforces systematic spacing and two-column separation that prevents the most common overlap defects.
Phase 1: Build with ExcalidrawAutomate
Create elements using the EA API via obsidian eval. EA handles text sizing and arrow binding automatically. The build ends by exporting all elements as a .excalidraw JSON file to /tmp/diagram.excalidraw.
Before writing EA code, read references/layout-heuristics.md Part 1 for grid-based coordinate planning.
Core Pattern
(async () => {
const ea = window.ExcalidrawAutomate;
ea.reset();
const s = ea.style;
s.roughness = 0; s.opacity = 100; s.fillStyle = 'solid';
s.fontFamily = 3; s.roundness = {type: 3};
// --- Style BEFORE each element ---
s.strokeColor = '#e2e8f0'; s.backgroundColor = '#1e40af'; s.fontSize = 16;
const boxA = ea.addText(200, 50, 'Label', {
box: 'rectangle', boxPadding: 20, boxStrokeColor: '#60a5fa'
});
// --- Arrows ---
s.strokeColor = '#475569';
ea.connectObjects(boxA, 'bottom', boxB, 'top', { endArrowHead: 'arrow' });
// --- Free text + structural lines ---
s.strokeColor = '#93c5fd'; s.fontSize = 20;
ea.addText(40, 210, 'SECTION TITLE');
// --- Export ---
const els = ea.getElements();
const json = {
type: 'excalidraw', version: 2, source: 'https://excalidraw.com',
elements: els,
appState: { viewBackgroundColor: '#1e1e1e', gridSize: 20 },
files: {}
};
require('fs').writeFileSync('/tmp/diagram.excalidraw', JSON.stringify(json, null, 2));
return els.length + ' elements exported';
})()
Key build rules (these restate the mechanical invariants above in context):
- Style before element —
ea.style.*applies to the NEXT element created addTextwith box returns the BOX id — use this forconnectObjects, not the text id- Stagger anchors when multiple arrows leave one shape (left/bottom/right, not all bottom)
- Diamond text ≤ 12 chars — diamonds have ~50% less usable area than rectangles
viewBackgroundColor—#1e1e1e(dark) or#ffffff(light), per Step 4
Read references/element-templates.md for the full EA API reference, raw JSON templates for Phase 2 fixes, and the binding checklist.
Mermaid shortcut: For simple flowcharts under 10 elements, use ea.addMermaid() instead. Only flowchart type produces native editable elements — other Mermaid types fall back to SVG images.
Phase 2: Validate (Mandatory)
After building, render to PNG, view it, fix defects. Up to 3 iterations — then save and report remaining issues.
ITERATION (repeat up to 3×):
1. CHECK — cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv run python check_overlaps.py /tmp/diagram.excalidraw
2. RENDER — cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv run python render_excalidraw.py /tmp/diagram.excalidraw \
--output /tmp/diagram.png --scale 2
View /tmp/diagram.png with the Read tool — HARD, every iteration.
3. JUDGE — Apply SOFT judgment. Design intent: correct patterns? Hero
dominant (if concept claims one)? Brushes from Painter's Toolkit
serving the concept? Defects: overlaps, crossings, uneven
spacing, text too small?
4. FIX — Edit .excalidraw JSON directly (Read → find element → Edit x/y).
Moving shapes does NOT break arrow binding (Excalidraw recalculates
from binding data, not coordinates).
Never change element IDs — this orphans connected arrows.
→ Next iteration, or proceed to Save if clean.
Read references/layout-heuristics.md Part 2 for fix strategies.
First-time setup (if renderer fails with missing deps):
cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv sync && uv run playwright install chromium
When to rebuild vs. JSON-edit: Positional fixes (move, resize, spacing) → edit JSON. Structural changes (add/remove elements, change connections) → rebuild from Phase 1 with ea.reset().
Phase 3: Save to Vault
Two paths — prefer ea.create(), fall back to manual write only if EA is unreachable.
Preferred: ea.create() via obsidian eval
(async () => {
const ea = window.ExcalidrawAutomate;
ea.reset();
const json = JSON.parse(require('fs').readFileSync('/tmp/diagram.excalidraw', 'utf8'));
json.elements.forEach(el => { ea.elementsDict[el.id] = el; });
ea.setView('new');
await ea.create({
filename: '<name>', foldername: '<folder>',
onNewPane: false, silent: true
});
return 'Saved to vault';
})()
ea.elementsDict is a documented public property used in official Excalidraw scripts.
Fallback: Manual Canonical Format
Only use when obsidian eval code="typeof window.ExcalidrawAutomate" returns empty (EA plugin unavailable). Obsidian checks format heuristics — any deviation causes silent corruption. Read references/save-format.md for the byte-exact template.
Post-Save Verification (Mandatory)
cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv run python verify_saved_diagram.py <vault-path>/<name>.excalidraw.md
Exits non-zero on format corruption or render mismatch. For the manual-fallback path, also compares the re-rendered PNG against /tmp/diagram.png to catch JSON corruption. If verification fails, regenerate using the canonical template from references/save-format.md — not the file you just wrote.
Embed in Wiki Notes
![[diagram-name]]
Place outside bilingual callouts (diagrams are language-neutral).
Delegation (Optional)
For complex diagrams, spawn a subagent for each mechanical phase to keep context clean. For simple diagrams or when subagents aren't available, follow the phases above directly.
When delegating, pass the agent file and relevant context. Each subagent reads its instructions and the reference files it needs:
- Build — pass the design spec (theme, zones, elements, connections, brushes chosen) and tell the subagent to read
agents/diagram-builder.md. Output:/tmp/diagram.excalidraw+ element count. - Validate — pass the diagram path and a 1-2 sentence design intent summary. Tell the subagent to read
agents/diagram-validator.md. Output: validated.excalidraw+ PNG at/tmp/diagram.png+ issues report. - Save — pass the diagram path, filename, folder, and embed target. Tell the subagent to read
agents/diagram-saver.md. Output: vault path of saved file.
Quality gate: After validation returns, view the PNG yourself before proceeding to Save. If it doesn't match your design intent, revise the spec and re-run Build, or give specific fix instructions to a new Validate pass.
Reference Files (Read on Demand)
references/color-palette.md— Semantic colors for light + dark modereferences/visual-patterns.md— 9 patterns with layout guidancereferences/painters-toolkit.md— Shape variety, subtitles, zone labels, containers, accents, separators, footers, size suggestionsreferences/element-templates.md— Full EA API reference + raw JSON templatesreferences/layout-heuristics.md— Grid planning (Part 1) + fix strategies (Part 2)references/depth-enhancements.md— Research, Multi-Zoom, Evidence (comprehensive only)references/save-format.md— Manual.excalidraw.mdcanonical template (fallback save path)references/plan_layout.py— Automatic coordinate computation for 20+ elementsreferences/verify_saved_diagram.py— Post-save verification (format markers + render check)
What to do yourself vs. delegate
Keep in-scope tasks yourself; route out-of-scope tasks to the dedicated skill.
| Task | Do yourself | Delegate to |
|---|---|---|
| Generate elements (EA API or raw JSON) | ✓ | — |
| Render to PNG (Playwright) | ✓ | — |
Check overlaps (check_overlaps.py) |
✓ | — |
Layout planning (plan_layout.py) |
✓ | — |
| Detect Obsidian theme | — | obsidian:obsidian-cli (via obsidian eval) |
| Find vault path / search existing notes | — | obsidian:obsidian-cli |
Write .excalidraw.md to vault |
✓ (direct filesystem write) — OR ea.create() |
— |
| Embed diagram in a wiki note | — | obsidian:obsidian-cli + obsidian:obsidian-markdown |
| Reload a changed diagram in Obsidian | — | obsidian:obsidian-cli |
Completion Format
✅ Created diagram → [vault-path/filename.excalidraw]
Pattern: [visual patterns used]
Elements: [count]
Validated: [iterations completed]
Blocked Format
⚠️ Diagramming blocked
Issue: [what went wrong]
To resolve: [specific action needed]