agentleFS
brunohappbrunohapp/skills-de-infografico/skills/architecture-drawer/skill/SKILL.md

architecture-drawer

Skill0 starsChanged 3 months agoMITView on GitHub

Use when asked to draw system architecture diagrams, generate technical architecture SVGs, or export architecture diagrams to editable PowerPoint presentations. Supports multi-layer diagrams with automatic layout validation and scoring (15-dimension evaluator catches collisions, overlaps, dangles, crossings, palette issues, low-contrast labels, misaligned peers).

The file

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 2 are distinctive, and 8 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Write a Python script calling $SKILL/svg_utils.py.

    line 42
  2. Very specific:

    Required: use drawer.check_collisions() to check overlaps.

    line 43
  3. Very specific:

    Call evaluate_svg(drawer) from $SKILL/evaluator.py.

    line 46
  4. Very specific:

    If the evaluation score is below 80, analyze the [FAIL] items in the report.

    line 53
  5. Very specific:

    Register edges: prefer drawer.connect(from_id, from_side, to_id, to_side, ...) — endpoints are taken from node border midpoints …

    line 76
  6. Distinctive:

    Font-size tiers: use only 3-4 tiers per diagram (title 20 / section header 14 / body 12 / note 10), adjacent tiers ≥1.15× apart (modular …

    line 91
  7. Distinctive:

    Stability: prefer relative layout logic (i.e.

    line 95
  8. Very specific:

    Every diagram generation is self-contained in its own subdirectory under output/.

    line 137
  9. Very specific:

    add_svg_to_slide: only supports shapes mode (no image rasterization).

    line 237
  10. Very specific:

    All detection/export capabilities parse the actually-rendered SVG (evaluate, don't assert), so they work equally well for raw add_element …

    line 242

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

SVG Architecture Drawer (Smart Version)

This Skill converts complex technical descriptions into structured SVG architecture diagrams. It integrates layout constraints, collision detection, connection/arrow connectivity validation, and quality evaluation, automatically identifying and guiding the correction of layout errors. The script directory (referred to as $SKILL below) is this skill's own scripts/ folder. From a generator script that lives next to its artifacts, resolve it relative to the script's own location (never hard-code an absolute path, which breaks on other machines):

import os, sys
_HERE = os.path.dirname(os.path.abspath(__file__))
# From evals/<name>/gen.py -> ../../scripts ; adjust depth for your layout.
_SKILL = os.path.normpath(os.path.join(_HERE, "..", "..", "scripts"))
if _SKILL not in sys.path:
    sys.path.insert(0, _SKILL)

Core Workflow: Generate-Evaluate-Correct

digraph eval_loop {
    "Generate gen.py" -> "evaluate_svg()" [label="run"];
    "evaluate_svg()" -> "score≥100 AND no [FAIL]?" [label="score"];
    "score≥100 AND no [FAIL]?" -> "Done" [label="yes"];
    "score≥100 AND no [FAIL]?" -> "auto_refine(drawer, max_iter=3)" [label="no"];
    "auto_refine(drawer, max_iter=3)" -> "score≥80?" [label="after n iterations"];
    "score≥80?" -> "Manual fix (coordinates/text)" [label="yes · ship"] ;
    "score≥80?" -> "Regenerate gen.py" [label="no · restart"];
}
  1. Content Parsing & Planning:
    • Identify layers, components, and flow direction.
    • Determine canvas dimensions (default 1200x800).
  2. Coding & Layout:
    • Write a Python script calling $SKILL/svg_utils.py.
    • Required: use drawer.check_collisions() to check overlaps; use the semantic API (below) to register nodes and edges so connections can be auto-validated.
  3. Quality Evaluation:
    • Call evaluate_svg(drawer) from $SKILL/evaluator.py.
    • Evaluation dimensions: ① Containment-aware element overlap detection (parent-child nesting does not count as a collision); ② boundary checks; ③ canvas coverage; ④ connection/arrow connectivity (endpoints must land on registered node borders, 12px tolerance; also detects degenerate zero-length edges and duplicate edges); ⑤ phantom anchor detection (nodes referenced by edges but invisible); ⑥ edge-routes-through-node (edges must not pass through the interior of a non-endpoint node, 3px inset); ⑦ edge crossing (two edge segments intersecting internally); ⑧ same-kind node minimum spacing (Euclidean distance between op/junction nodes ≥ 14px); ⑨ font-size tier detection (parse SVG to extract all font-size values, deduplicate to ≤4 tiers, adjacent tiers must be ≥1.15× apart — prevents accidental micro-steps like 11/12/13/14); ⑩ palette detection (accent colors ≤8 warning, ≤12 hard limit; coexistence of very dark L<0.2 and very light L>0.8 accents is flagged as a conflict; background defaults to light); ⑪ text overflow detection (parse all <text> elements' true geometry, estimate text width by font metrics: overflowing the canvas = FAIL, text wider than its container [smallest rectangle containing the text center] = WARN — closes the blind spot of text drawn via add_element that doesn't enter bboxes and is invisible to collision/boundary checks); ⑫ composition quality budget (ported from fireworks assess_composition: ≤2 bends per edge, path stretch ratio ≤1.35, container gutter ≥20px, shortest path segment ≥16px; text as a measurable obstacle — edge segments passing through a <text> bbox = FAIL, systematically eliminating "text crossed/covered by edges"; gutter is only checked for nodes fully contained within a container, cross-band nodes are not false-flagged). ⑨⑩⑪⑫ parse the actual SVG rather than relying on API calls, so they work equally well for raw add_element drawing.
    • ⑬ text-vs-shape & text-vs-text overlap detection (check_text_overlaps): parses every <text> bbox (center model, dominant-baseline="central") against all visible circles/rects/polygons/lines/paths AND against other <text> — closes the registry blind spot where bbox=False text/shapes and add_element shapes are invisible to check_collisions. Legend/background shapes, the full-canvas bg rect, and rects fully containing the text (intentional in-box labels) are exempt. Like ⑨⑩⑪⑫, this parses the actual SVG rather than trusting the API.
    • ⑭ same-kind peer alignment (check_alignment): two SAME-SIZED same-kind visible nodes that read as a row or column (strong overlap on the perpendicular axis) yet share NEITHER a top/bottom/left/right edge (within 5px) NOR a center line (within 15% of the shorter side) are flagged — the "align to shared edges" layout principle. Differently-sized peers are skipped (a row of varied components legitimately staggers).
    • ⑮ text-on-fill contrast (check_contrast): WCAG 2 contrast ratio between each <text>'s fill and the fill of the smallest <rect> containing it — FAIL below 3:1 (large-text floor), WARN below 4.5:1 (AA for normal text) / 3:1 large (≥24px, or ≥18.5px bold). Only text on a non-neutral (accent) fill is measured; accent-colored text on a white/neutral canvas (category labels, captions) is a typographic choice, not a fill defect, and is skipped. Replaces the former "manual review recommended" placeholder.
  4. Auto-Correction:
    • If the evaluation score is below 80, analyze the [FAIL] items in the report.
    • Connection issues (dangles / Degenerate edge / overlaps): use drawer.connect(...) to let endpoints auto-snap to node borders; avoid manually computing offset coordinates.
    • phantom (phantom anchors): the node referenced by an edge is invisible → give it a real fill/stroke, or use the distinct-port pattern to connect to a visible junction.
    • routes through node: an edge cuts through an intermediate node → reroute via orthogonal bypass channels, or relay through a junction (see distinct-port pattern), keeping a ≥20px gap from the intermediate node.
    • cross (edge crossings): adjust node layout or routing channels so edges don't intersect (reference fireworks' zero-crossing budget).
    • too close: same-kind nodes are clustered → increase spacing or enlarge the canvas.
    • Arrow position: connect() auto-retracts by marker_tip_depth — retraction = (markerWidth − refX) × stroke_width, derived from the marker dimensions registered by arrow_head(), so the arrow tip lands exactly on the target border (neither poking in nor leaving a gap). For custom markers, pass the real dimensions via arrow_head(id, color, marker_width=, ref_x=) — no manual tweaking needed.
    • font (font sizes): more than 4 distinct tiers after dedup → converge to 3-4 tiers (title/body/note); near-overlapping tiers (ratio <1.15, e.g. 11/12) → merge into one tier. Recommended modular scale: 20 / 14 / 12 / 10 (all steps ≥1.15). This matches the tier count measured in each ink-graph style.
    • palette: accent count >8 → revert op fills to neutral white, or switch to a single-hue scheme (S1 Monochrome Blue, only 5 accents) so layer fills + one unified border carry the palette; >12 → trim to a preset scheme. Luminance conflict (very dark + very light coexist) → unify into one brightness family. Non-light background → apply white by default; dark themes must declare set_background()/bg=. See references/design_specs.md for the 4 preset schemes (S1–S4) and when to use each.
    • Layout issues: adjust component coordinates, spacing, or scale ratio, then regenerate.
    • text overflow: text exceeds the canvas → shorten the copy or shift the start point left; text wider than its card/container → shorten, auto-wrap by container width (greedy word-wrap), or widen the container. Note that <text> does not enter bboxes by default, so collision/boundary checks can't see it — this detection fills that gap.
    • text overlap (text on a shape or another text): a label sits on top of a circle/triangle/arc/line or collides with a neighboring label → move the label clear of the shape (place it above/below the icon, not on it) or shorten it. auto_refine cannot fix this (no geometry handle for raw add_element text) — adjust coordinates manually. This catches overlaps check_collisions misses because bbox=False text/shapes and add_element shapes bypass the collision registry.
    • contrast (low text-on-fill contrast): a label doesn't read against its accent card (ratio <3:1 FAIL, <4.5:1 WARN for normal text) → darken/lighten the text fill toward the channel extreme (pure #000000/#ffffff on a mid-tone card is always safe), or switch the card to a lighter tint of the same hue so a dark label clears AA. Note: accent-colored text on a neutral canvas is a deliberate category/heading choice and is not flagged — only labels on accent fills are. auto_refine does not touch colors; adjust manually.
    • alignment (misaligned same-size peers): two same-sized same-kind nodes in a row/column share no edge/center line → nudge one onto the other's top/bottom (row) or left/right (column) edge, or onto a shared center line. Differently-sized peers are exempt (they legitimately stagger). auto_refine does not handle alignment yet — adjust coordinates manually.
    • composition gutter (insufficient container margin): node too close to the container edge → push the node toward the container center; or call auto_refine(drawer) (below) to iteratively auto-correct.
    • auto_refine(drawer, target_score=100, max_iter=3): reads the evaluate_svg report and auto-corrects programmable issue categories (gutter → nudge node toward container center; too close → spread along the primary axis), looping until the target is met or iterations are exhausted. Returns (score, report, fixes). Complex fixes (dangles/cross/route-through) still need manual intervention — auto_refine only handles geometric micro-adjustments.

Node & Edge Semantics

For the evaluator to "see" connections, drawing code must register connectable rectangles as nodes and connections as edges:

  • Register nodes: drawer.rect(..., node_id="op1", node_kind="op") — draws a rectangle and registers a node simultaneously. node_kind can be "op" | "layer" | "block" | "region"; used for provenance only, not for validation.
  • Circle nodes (junctions/markers): drawer.circle(cx, cy, r, ..., node_id="jn", node_kind="junction") — draws a visible circle and registers a square Node of side 2r as a snap anchor; endpoints landing at the center register distance 0. Ideal for bus junctions and port markers.
  • Register edges: prefer drawer.connect(from_id, from_side, to_id, to_side, ...) — endpoints are taken from node border midpoints ("top"|"bottom"|"left"|"right"), so arrows always land precisely on the node edge. Options: dashed=True (dashed, e.g. lowering/bypass flows), as_curve=True + curve_dir="left"|"right" (curve), edge_label= (annotation).
  • Dashed rendering: dashed= is available on all primitives — rect, circle, line, path, and connect. Pass dashed=True for the standard "6,3" pattern, or dashed="4,3" for a custom dash pattern. This replaces the old extra='stroke-dasharray="..."' spelling (which still works for backward compatibility).
  • Low-level entry: drawer.line(..., register_edge=True) / drawer.path(..., register_edge=True, start=..., end=...) can also manually register edges (for curves, start/end are the semantic endpoints; d can be any path).
  • group(transform) context: nodes/edges/bboxes drawn inside with drawer.group("translate(100,50) rotate(30)"): are registered in absolute coordinates via the accumulated affine matrix, so local coordinates inside a group are also validated. Supports chained matrix()/translate()/scale()/rotate()/skewX()/skewY().
  • Advanced shapes (ported from ink-graph shapes.md, local coordinates via <g transform>): drawer.database(x,y,w,h,...) (cylinder, top ellipse depth=min(8,h0.12)), drawer.decision(...) (diamond, four points around center), drawer.hexagon(...) (gateway, 25% corner insets), drawer.component(...) (with left-edge double tabs), drawer.cloud(...) (multi-lobe cubic curves). All accept node_id/role/label, register nodes consistently with rect()/circle(), and support connection snapping. Text centering uses dominant-baseline="central" (exact, replacing the old y+0.35fs approximation).
  • Semantic role role= (optional): rect/circle/connect/line/path all accept role="node|edge|decoration|legend|background". Elements set to decoration/legend/background emit a data-graph-role attribute and are excluded from business checks (spacing, collision, palette count) — used for decorative layers (rail casings, background textures, legends). Default node/edge means business elements.
  • Math formulas with sub/superscripts: drawer.formula(x, y, markup, font_size=, fill=, anchor=, weight=) renders genuine <tspan> baseline shifts — unlike text() (which HTML-escapes content and can only show literal underscores/carets). Markup: _{...} → subscript, ^{...} → superscript; baseline auto-resets between tokens so multiple indices align (e.g. "F_{k} = MS^{↑}_{k} + g_{k}"). Default monospace family + bold for an equation look; pass weight="normal" for inline annotations. The sub/superscript glyph sizes (~0.72×) are derivative of the parent text size and are excluded from the font-tier count (see check_font_scale), so formulas do not inflate the 3–4-tier typography budget. (Note: svg2pptx concatenates <tspan> text flat — use image mode if you need exact subscript fidelity in PowerPoint.)

"Invisible anchor" anti-pattern (now forcefully blocked): it used to be possible to create fill="none" stroke="none" invisible rectangles to cheat the connection validation — the evaluator would pass, but the human eye would see dangling lines. Now check_phantom_anchors() detects any node referenced by an edge that is invisible (fill=none ∧ stroke=none/opacity=0/zero-size) and flags it as FAIL. When you need a "bus rail" or cross-layer channel, use the distinct-port pattern below to connect to a visible junction.

Distinct-port / junction pattern (cross-layer aggregation, side channels): place visible circular junction nodes at the channel position, connect each real component to the junction with a short solid line connect(layer, "left", junction, "right"), then chain them into a rail with dashed lines connect(junction, "bottom", junction2, "top", dashed=True). This way each rail segment lands between real visible nodes — passing validation while remaining clear to humans.

Tip: container-type large rectangles (Module/Layer) enter bbox collision and coverage stats by default; for interior small elements (text, operation nodes), pass bbox=False to avoid false overlap reports or inflated coverage.

Design Specifications

  • Font-size tiers: use only 3-4 tiers per diagram (title 20 / section header 14 / body 12 / note 10), adjacent tiers ≥1.15× apart (modular type scale). Exceeding this triggers an evaluator warning.
  • Palette: pick by information need (see references/design_specs.md and the per-type table in references/diagram_types.md) — S2 Categorical when 2–4 classes / branches / roles need hue, S3 Semantic for fixed component types (cloud / network), S4 Duotone for one focal element, S1 Monochrome Blue only as the fallback for pure layering with no categorical role. Do not reflex-default to S1 — it makes every diagram blue. All schemes pre-verified (accent ≤12, no luminance clash). Op cards stay fill="white"; color lives in layer fills + borders only. Background defaults to white (SVGDrawer(bg="#FFFFFF")); only use set_background() for dark themes.
  • Color & typography: see references/design_specs.md.
  • Shapes & layout per type: when the user names a diagram type (architecture / flowchart / ML model / ER / sequence / swimlane / network), apply the matching preset in references/diagram_types.md — it maps each semantic role to a primitive (rect / database / decision / hexagon / component / cloud) and gives direction + spacing defaults that pass the evaluator.
  • Stability: prefer relative layout logic (i.e. compute new component positions based on known component coordinates).
  • Entity escaping: SVG is strict XML — never use HTML entities in text (&middot;/&mdash; etc. are rejected by the parser). Use Unicode characters (· —) or html.escape instead.

Example: Generation with Evaluation

import sys
# Resolve the skill scripts dir relative to this file (see $SKILL note above).
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "scripts"))
from svg_utils import SVGDrawer, save_svg
from evaluator import evaluate_svg
from pathlib import Path
# Write outputs next to this script (see "Output Layout Convention" below).
OUT = Path(__file__).resolve().parent

drawer = SVGDrawer(1200, 800, bg="#FFFFFF")   # white background is the default; change only for dark themes
drawer.arrow_head("arrowhead", "#333")

# Nodes (Scheme S1 Monochrome Blue — L1 tier; see design_specs.md for the full library)
drawer.rect(100, 100, 90, 34, fill="#D5E1EB", stroke="#1B3A5C",
            node_id="a", node_kind="op", bbox=False)
drawer.rect(300, 100, 90, 34, fill="#D5E1EB", stroke="#1B3A5C",
            node_id="b", node_kind="op", bbox=False)

# Edges: endpoints auto-snap to node borders, arrows land precisely on the edge
drawer.connect("a", "right", "b", "left",
               stroke="#1B3A5C", marker_end="arrowhead", edge_label="value")

# Evaluation (includes connection/arrow validation)
score, report = evaluate_svg(drawer)
print(f"Quality Score: {score}")
for line in report:
    print(line)

if score >= 80:
    save_svg(drawer.render(), str(OUT / "diagram.svg"))
else:
    print("Score too low, need adjustment.")

Output Layout Convention

Every diagram generation is self-contained in its own subdirectory under output/. One diagram = one directory; the generator script and its SVG/PNG/PPTX triplet live together.

output/<timestamp>_<name>/
- gen_<name>.py        # generator script (version-controlled source)
- <name>.svg           # -
- <name>.png           #  |- triplet, regenerated in place on each run
- <name>.pptx          # -

Rules:

  • Script and outputs are co-located. The gen_*.py lives inside its output/<ts>_<name>/ directory — never in the repo root, never scattered away from its artifacts. Deleting the directory removes script + outputs together.
  • Write to the script's own directory, not a fresh timestamped dir per run — re-running refreshes the triplet in place rather than accumulating duplicate directories:
    from pathlib import Path
    OUT = Path(__file__).resolve().parent   # this script's own directory
    NAME = "<name>"
  • The <timestamp>_<name> directory name is frozen at creation (a born-on date); it is not regenerated on each run.
  • Always emit the full triplet — SVG (save_svg), PNG (rasterize_svg, wraps rsvg-convert), and PPTX (svg2pptx.svg_to_pptx) — so the directory is self-describing.
  • Artifacts are gitignored by extension (**/*.svg, **/*.png, **/*.pptx); the gen_*.py scripts stay version-controlled. Never gitignore the whole output directory — that hides the scripts.

The save/rasterize helpers

save_svg(), rasterize_svg(), and svg2pptx.svg_to_pptx() write wherever you ask — they create parent directories as needed and impose no layout constraint. A prior version enforced an output/<task>/ directory at the library boundary (validate_output_path / OutputPathError); that was removed for the public release because it refused legitimate cross-project and temporary paths.

from svg_utils import save_svg, rasterize_svg

save_svg(content, OUT / "diagram.svg")      # writes, mkdir -p the parent
rasterize_svg(OUT / "diagram.svg", OUT / "diagram.png", width=1200)
  • save_svg(content, filename) — writes SVG content to filename, creating parents; returns the resolved path.
  • rasterize_svg(svg_path, png_path, width) — runs rsvg-convert -w <width>; creates parents; returns the PNG path.
  • svg2pptx.svg_to_pptx(svg, pptx_path, config=None) — converts to PPTX; creates parents.

Prefer these wrappers over a raw subprocess.run(["rsvg-convert", ...]) so path handling stays uniform.

SVG to PPTX Export (svg2pptx)

After generating an SVG, you can export it to an editable PowerPoint file with one call. The module $SKILL/svg2pptx.py parses SVG elements into native PowerPoint shapes (rectangles, ovals, connectors, text boxes, freeforms) rather than embedding an image — so each element can be individually resized, recolored, and edited in PowerPoint/Keynote/LibreOffice. Inspired by the svg2pptx project.

Two Export Modes

ModeParameterEffectUse Case
shapes (default)mode="shapes"Each element → an independent editable shapeArchitecture diagrams you want to fine-tune in PPT
imagemode="image"Rasterize to PNG and embed (100% visual fidelity)Complex SVGs for display only, no editing needed

API

# sys.path was set above in the Example section ($SKILL = scripts directory)
from svg2pptx import svg_to_pptx, PptxConfig, save_pptx

# Option 1: SVG string → PPTX (most common, takes drawer.render())
svg_to_pptx(drawer.render(), OUT / "diagram.pptx")

# Option 2: SVG file → PPTX (file must end with .svg, otherwise parsed as an SVG string)
svg_to_pptx(OUT / "diagram.svg", OUT / "diagram.pptx")

# Option 3: Export directly from an SVGDrawer (equivalent to Option 1)
save_pptx(drawer, OUT / "diagram.pptx")

# Custom config: 16:9 slide, 2x scale, shapes mode
svg_to_pptx(OUT / "diagram.svg", OUT / "diagram.pptx",
            config=PptxConfig(slide_w=13.333, slide_h=7.5, scale=2.0))

# Image mode (rasterized embed, requires rsvg-convert)
svg_to_pptx(OUT / "diagram.svg", OUT / "diagram.pptx", config=PptxConfig(mode="image"))

# Add to an existing presentation's slide (no new file created)
# Note: add_svg_to_slide only supports shapes mode, not image rasterization
from svg2pptx import add_svg_to_slide
add_svg_to_slide(drawer.render(), slide, x=1.0, y=0.5, scale=0.8)

SVG → PPTX Element Mapping

SVG ElementPPTX ShapeNotes
<rect rx=0>RectangleAuto shape; rotates correctly inside a rotated <g>
<rect rx>0>Rounded RectangleCorner radius auto-mapped; rotation also handled
<circle> / <ellipse>OvalEllipse/circle; rotation handled correctly
<line>Connector (Straight)Straight-line connector
<polygon>Freeform (closed)Polygon → freeform
<polyline>Freeform (open)Polyline → freeform
<path>FreeformBezier/Arc auto-flattened to line segments (curve_tolerance controls precision)
<text> / <tspan>Text BoxPreserves font/size/color/alignment, CJK works; <tspan> child text is auto-concatenated
<g transform>Coordinate transformAccumulated affine matrix (translate/scale/rotate) applied to all child shapes
marker-endFreeform triangleArrow auto-rendered: draws a triangle at the segment endpoint based on marker geometry
fill-opacityTransparencyImplemented via <a:alpha> XML injection (python-pptx does not support this natively)
stroke-dasharrayDashed lineprstDash or custDash XML injection

Limitations

  • Gradients are not supported (the first color is used).
  • Filters/effects (blur, shadow) are not supported (shape shadows are disabled by default).
  • Bezier curves are flattened to line segments (lower curve_tolerance = smoother, default 1.0px).
  • Font scaling: font size scales proportionally with the fit-to-slide scale (Pt(fs * scale * 72/96)) — i.e. a large canvas mapped to a small slide shrinks text, and vice versa. The text-to-box ratio always stays consistent. To fix the font size, set scale=1.0 and adjust slide_w/slide_h yourself.
  • add_svg_to_slide only supports shapes mode (no image rasterization); for image mode use svg_to_pptx.
  • Image mode requires rsvg-convert (installed on this system); shapes mode only requires python-pptx.

Capabilities

All detection/export capabilities parse the actually-rendered SVG (evaluate, don't assert), so they work equally well for raw add_element drawing.

#CapabilityDescription
①Containment-aware collisionParent-child nesting does not count as a collision
②Phantom anchor detectionNodes referenced by edges but invisible → FAIL
③Edge × edge crossingTwo edge segments intersecting internally
④Marker depth auto-derivation(markerWidth−refX)×stroke
⑤Font-size tier detectionParses SVG, ≤4 tiers, adjacent tiers ≥1.15× apart
⑥Palette detectionAccent ≤8/12, background defaults to light
⑦Curve bezier/arc samplingPorted from fireworks path_routes
⑧Luminance conflict by channelFill/stroke checked separately for dark+light coexistence
⑨Transform matrix accumulationgroup() context
⑩data-graph-role semantic rolesdecoration/legend/background skip business checks
⑪Coverage bbox unionSweep-line algorithm
⑫Text overflow detectionParses <text> geometry
⑬Composition quality budgetbend≤2/stretch≤1.35/gutter≥20/segment≥16 + text as obstacle
⑭Node shape librarydatabase/decision/hexagon/component/cloud
⑮Barycenter crossing minimizationPorted from DiagramForge: reorder by barycenter within layers
⑯auto_refine auto-correctionReads eval report, iteratively fixes gutter/spacing by issue code
⑰SVG→PPTX exportNative editable shapes + rasterized image dual modes, arrow rendering, Bezier/Arc flattening, transparency/dash injection
⑱Text-vs-shape & text-vs-text overlapParses rendered SVG: <text> bbox vs visible circles/rects/polygons/lines/paths + text vs text; closes the bbox-registry blind spot (bbox=False/add_element)
⑲Formula rendering (sub/superscript)drawer.formula() emits real <tspan> baseline shifts for _{}/^{} markup; evaluator strips markup in width estimates and counts only <text>-tier font sizes so subscripts don't inflate the tier budget
⑳Text-on-fill contrast (WCAG 2)check_contrast: ratio of each <text> fill vs its smallest containing <rect> fill — FAIL <3:1, WARN <4.5:1 (AA) / 3:1 large (≥24px / ≥18.5px bold); only accent fills measured, accent text on neutral canvas skipped
㉑Same-kind peer alignmentcheck_alignment: same-sized same-kind nodes in a row/column must share a top/bottom/left/right edge (±5px) or a center line (±15%); differently-sized peers exempt

References & Acknowledgments

This Skill's geometry/connection detection draws on the following open-source projects (their references and validator implementations were actually studied):

  • ink-graph (qaz1230sp/ink-graph): its references/pitfalls.md #2 (arrow occluded by node → retract endpoint 8px), #3/#10 (edge crossing through node → 20px gap bypass), #17 (fan-out alignment), #26 (marker size proportional to stroke), #29 (fan-out/fan-in + junction dot); its references/shapes.md dominant-baseline="central" centering, its references/layout-rules.md grid/spacing rules. Measured each of its style-*.md at exactly 3-4 font tiers, 4-13 palette colors — empirical basis for the font-tier/palette thresholds. (These files live in the ink-graph repo, not this skill.)
  • fireworks-tech-graph (yizhiyanhua-ai/fireworks-tech-graph): its references/composition-quality-contract.md (executable budget: zero crossings/≤2 bends/≥40px node spacing/≥20px container gutter); its scripts/validate_svg.py find_collisions + segment_hits_bounds (path sampling vs node bbox), data-graph-role semantic roles, transform matrix accumulation, "evaluate, don't assert" (parse actual SVG rather than trusting API calls). (Files live in the fireworks repo, not this skill.)
  • svg-animations (supermemoryai/skills): SMIL/CSS animation basics and stroke-dasharray stroke animation recipes (this Skill does not enable animation yet, reserved for later).
  • svg-design (tryopendata/skills): primitive-first (circles use <circle>), stroke-linecap="round", strict XML with no HTML entities, and other hygiene conventions.
  • svg2pptx (benouinirachid/svg2pptx): architectural blueprint for the PPTX export module. Its "SVG element → PowerPoint native editable shape" philosophy (rect→rectangle, circle→oval, line→connector, path→freeform, text→textbox), Config dataclass design, build_freeform + add_line_segments usage, and Bezier flattening tolerance parameter were all adapted into the self-contained scripts/svg2pptx.py module (which adds arrow marker rendering, fill-opacity transparency injection, stroke-dasharray dash injection, and an image rasterization fallback mode).

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.