Clean architecture and system-design diagrams as SVG — designed to be authored by LLMs. Single-spine arrow routing, 16:9 aspect-ratio-aware, zero deps. Includes a Claude Code skill bundle.
SaferSkills independently audited spinediagrams (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Produces fixed-width (1600 px) SVG architecture diagrams with:
says ICON CHECK FAILED, the diagram is NOT done: resolve every listed node (run /get-icon <technology>, set an explicit icon key, or rename the node to the canonical technology name), re-render, and repeat until the report says Icon check: OK. Suppress an icon (spine: 5th node field ""; flow: "icon": "" on the node) only for genuinely non-technology nodes: people, teams, abstract concepts. Never present an SVG whose last render reported missing icons. (If you rendered via import instead of the CLI, print the check yourself: from svg_engine import icon_report; print(icon_report(d)).)
<skill_dir>/scripts/svg_engine.pyImport or call it directly:
# Option A: import
import sys
sys.path.insert(0, "<skill_dir>/scripts")
from svg_engine import Diagram
d = Diagram(config) # config is a dict (see format below)
svg = d.render()
with open("output.svg", "w") as f:
f.write(svg)
# Option B: CLI (config must be written to a JSON file first)
# python <skill_dir>/scripts/svg_engine.py config.json output.svgconfig = {
"title": "Main Title — Optional subtitle", # " — " (em dash) splits title from subtitle
"num_cols": 6, # optional, default 6; increase for more lanes per row
"aspect": "16:9", # optional, default "16:9". Also accepts "4:3" or a
# numeric width/height ratio. Canvas pads to this
# ratio by growing the spine, which spaces arrow
# labels further apart.
# ── Lanes (containers) ──────────────────────────────────────────────────
# Each key becomes the lane identifier used in nodes/connections.
# For well-known vendors, just use the preset key (no colours needed).
# For custom lanes, supply bg / border / header_bg.
"lanes": {
"sf": { # preset key -> colours auto-filled
"label": "Salesforce",
"col": 0, "colspan": 2, "row": 0
},
"gcp": {
"label": "GCP Platform",
"col": 0, "colspan": 2, "row": 1
},
"custom": { # custom lane -> supply colours
"label": "My Service",
"col": 2, "colspan": 1, "row": 0,
"bg": "#f0fdf4", "border": "#16a34a", "header_bg": "#bbf7d0"
}
},
# ── Nodes ───────────────────────────────────────────────────────────────
# Each list entry: ["label", "status"] or with optional fields:
# ["label", "status"]
# ["label", "status", "category"]
# ["label", "status", "category", True] # primary (bold + border ring)
# ["label", "status", "category", True, "icon"] # explicit icon key override
# Status values: existing | new | transitioning | retiring | operational | readonly
"nodes": {
"sf": [["CRM", "existing"], ["Billing", "transitioning"]],
"gcp": [["Cloud SQL", "new"], ["API Gateway", "new"]],
"custom": [["Auth Service", "new"]]
},
# ── Connections ─────────────────────────────────────────────────────────
# Each entry: [src_lane, src_node_label, dst_lane, dst_node_label, edge_label]
# src/dst_node_label must exactly match a node label in that lane.
# Edge label is shown inline on the routing line.
"connections": [
["sf", "Billing", "gcp", "Cloud SQL", "Pub/Sub sync"],
["gcp", "API Gateway", "sf", "CRM", "Write-back"]
]
}The em dash inside the title string is API syntax, not prose. The engine literally splits the title field on — to separate title from subtitle.
The diagram has up to 3 rows and up to num_cols columns (default 6).
Assign each lane a col (0-based), colspan, and row (0 = top, 1 = middle, 2 = bottom). Lane widths in a row must not overlap: col + colspan for each lane must stay within num_cols. Row 2 is optional; omit it for a classic 2-row diagram.
Suggested 2-row layout:
| Row | Col 0-1 (span 2) | Col 2 | Col 3 | Col 4 | Col 5 |
|---|---|---|---|---|---|
| 0 | Large source (e.g. SF) | Mid-tier A | Mid-tier B | Mid-tier C | (empty) |
| 1 | Large target (e.g. GCP) | Ext A | Ext B | Ext C | Ext D |
3-row layout (classic 3-tier: frontend / backend / data + externals):
| Row | Use it for |
|---|---|
| 0 | User-facing surfaces (browser, mobile, public APIs) |
| 1 | Backend services / orchestration / business logic |
| 2 | Data stores + external SaaS dependencies |
Connections route through one of two spines, or, for skip-row connections, through a margin-sidestep channel:
Sidestep side (left vs right) is chosen automatically by the midpoint of the source/destination columns: connections living mostly on the left half of the canvas route through the left margin, the rest through the right. Each sidestep gets its own X channel inside the margin; the margin widens automatically to accommodate multiple sidesteps per side.
Use sidestep connections sparingly. They look visually distinct (longer paths, longer journey for the eye) which honestly conveys "this skips the middle layer." If you have many row 0↔2 connections, consider whether row 1 should mediate them in reality.
| Key | Label default | Colour theme |
|---|---|---|
sf | Salesforce | Indigo |
bench | Bench App | Green |
hubspot | HubSpot | Orange |
zapier | Zapier | Purple |
gcp | GCP Platform | Sky blue |
aws | AWS | Amber |
azure | Azure | Blue |
stripe | Stripe | Pink |
qbo | QuickBooks Online | Yellow |
postgres | PostgreSQL | Steel blue |
redis | Redis | Red |
kafka | Kafka | Deep purple |
okta | Okta | Amber |
slack | Slack | Violet |
twilio | Twilio | Rose |
ses | Amazon SES | Deep orange |
docusign | DocuSign | Slate |
saas | Third-Party SaaS | Cool grey |
generic | (any) | Neutral grey |
For any key not in this list, supply explicit bg / border / header_bg.
By default, all nodes use a consistent light gray background (#f1f5f9) regardless of status. This keeps focus on the structure and connections rather than lifecycle state.
To enable legacy per-status node coloring, set "node_status_colors": true:
config = {
"node_status_colors": true, # nodes colored by status (cyan=new, amber=transitioning, etc.)
# ...
}| Status | Meaning | Legacy colour |
|---|---|---|
existing | Unchanged, currently live | Slate |
new | Being built / not yet live | Cyan |
transitioning | Partially moved / dual-write | Amber |
retiring | Being decommissioned | Red |
operational | Fully live on new platform | Green |
readonly | Still present but no writes | Light grey |
The bottom legend is hidden by default (since node colors are uniform). Configure it with the legend key:
# Hide legend (default when node_status_colors=false)
"legend": false
# Show legacy status legend (automatic when node_status_colors=true)
"legend": "default"
# Custom legend for diagram-specific meanings
"legend": [
("#0891B2", "API endpoint"),
("#B45309", "Background job"),
("#15803D", "Database", "Stores persistent state"), # optional tooltip
]Custom legend entries: (color, label) or (color, label, tooltip_description).
Nodes automatically display technology icons when the engine detects a known technology in the label. Icons appear to the left of each node label in both the node itself and the lane header strip.
The engine auto-detects icons from node labels using:
.py → Python, .js → JavaScript, .ts → TypeScript, .sh → BashSet "show_icons": false in the config to hide all icons:
config = {
"title": "My Diagram",
"show_icons": false,
# ... rest of config
}To force a specific icon (or suppress auto-detection for one node), use the 5-element node format:
["My Service", "new", "", True, "docker"] # force Docker icon
["Legacy App", "retiring", "", False, ""] # suppress icon for this nodeIf a technology icon is missing, use /get-icon <technology> to find and add it. The skill searches Simple Icons, Iconify, and the web, then outputs ready-to-paste entries for svg_engine.py.
| Category | Examples |
|---|---|
| Languages | python, javascript, typescript, go, rust, java, ruby, swift, kotlin |
| Frameworks | react, vuedotjs, angular, nextdotjs, django, rails, spring, flask |
| Databases | postgresql, mysql, mongodb, redis, elasticsearch, sqlite, cockroachdb |
| Cloud | googlecloud, amazonaws, microsoftazure, vercel, netlify, heroku |
| DevOps | docker, kubernetes, terraform, ansible, jenkins, gitlab, github |
| Messaging | slack, discord, twilio, telegram |
| Analytics | amplitude, segment, datadog, newrelic, sentry |
The full list is in svg_engine.py under TECH_ICONS (221 icons).
""."num_cols": 8 (or higher); column widths shrink proportionally.mcp__cowork__present_files (Cowork) or tell the user the file path (Claude Code).~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.