
# Claude Code's GCP Architecture Diagram Skills Improvement — Automating Coordinate Validation and Discovering Design Patterns
This page has been translated by machine translation. View original
Introduction
In the previous article, I created a skill that automatically generates Draw.io files just by telling Claude Code "draw a GCP architecture diagram."
Icon display and container nesting worked correctly, but when I actually used it on a real project, several problems emerged. Arrows were passing over labels making text unreadable, element placement was linear with no logical grouping, and flow diagram step numbers were meaningless without consulting the legend.
This article introduces how I solved these problems. Specifically:
- Coordinate validation script — A Python tool that automatically detects element overlaps
- Draw.io z-order problem — The root cause of arrows being drawn on top of labels, and how to handle it
- Inline edge labels — Making step numbers self-documenting by adding descriptions
- Layout improvements — Logical grouping of related services
Problem: Finding coordinate overlaps manually is tedious
When I instruct the skill to "draw an architecture diagram for a RAG chatbot," it generates a Draw.io file like the following.
Google Chat → Chatbot → Knowledge Search → AI Answer Generation
↓
Conversation History
Looking at the generated XML, each element's coordinates are defined as relative positions from the parent container:
<!-- svc-cf-bot coordinates are (80, 130) inside grp-project (x=280, y=130) -->
<!-- → absolute coordinates: (360, 260) -->
<mxCell id="svc-cf-bot" parent="grp-project" vertex="1">
<mxGeometry x="80" y="130" width="34" height="42" as="geometry" />
</mxCell>
The problem is that as elements increase, it becomes difficult to mentally calculate absolute positions from relative coordinates. Is one icon's label overlapping another icon? Are an arrow's waypoints avoiding the label area? Manual verification is impractical.
Solution 1: Coordinate validation script
I created a Python script that parses Draw.io XML and automatically detects overlaps between elements.
How it works
@dataclass
class BBox:
"""Axis-aligned bounding box"""
x: float
y: float
width: float
height: float
@property
def right(self) -> float:
return self.x + self.width
@property
def bottom(self) -> float:
return self.y + self.height
def overlaps(self, other: BBox) -> bool:
"""AABB overlap test"""
if self.x >= other.right or other.x >= self.right:
return False
if self.y >= other.bottom or other.y >= self.bottom:
return False
return True
The script's processing flow is as follows:
1. XML parsing — Extract all mxCell elements from the .drawio file and retrieve coordinates, styles, and parent-child relationships
2. Recursive absolute coordinate resolution — Convert relative coordinates from parent containers to absolute coordinates
def _resolve(elem_id: str) -> tuple[float, float]:
elem = elements[elem_id]
if elem.resolved:
return (elem.abs_x, elem.abs_y)
parent_abs = _resolve(elem.parent_id)
elem.abs_x = parent_abs[0] + elem.rel_x
elem.abs_y = parent_abs[1] + elem.rel_y
elem.resolved = True
return (elem.abs_x, elem.abs_y)
3. Label bounding box estimation — GCP icons use verticalLabelPosition=bottom to display labels below the icon. The label area is estimated from font size and character count
# CJK characters are approximately 9px each, Latin characters approximately 7px (at 11px font)
avg_char_w = 9.0 if any(ord(c) > 0x2E80 for c in text) else 7.0
text_width = len(text) * avg_char_w * (font_size / 11.0)
4. Edge routing approximation — Edges with explicit waypoints calculate segments precisely, while auto-routed edges are approximated as Z-shaped orthogonal paths
5. Four types of overlap detection:
- Edge–label intersections
- Container overlaps
- Icon proximity (minimum gap of 10px)
- Label–label overlaps
How to use
uv run python scripts/validate_drawio.py docs/gcp-rag-chatbot.drawio \
--export-coords docs/gcp-rag-chatbot-coords.json \
--ignore grp-project:lane-b-header
Example output:
Elements: 33 (11 edges)
[EDGE-LABEL] edge-4 overlaps svc-cf-bot label (gap: -3.5px)
[EDGE-LABEL] edge-5 overlaps svc-cf-bot label (gap: -8.2px)
[EDGE-LABEL] edge-rag-link overlaps svc-logging label (gap: -5.1px) [approx]
The [approx] mark indicates detections from auto-routing approximation, which may be false positives and require visual confirmation. --ignore ID1:ID2 can exclude intentional overlaps (such as swim lane headers spanning containers).
The JSON file output by --export-coords records the relative coordinates, absolute coordinates, and bounding boxes of all elements. It is useful for checking coordinates one by one during debugging.
Problem: Arrows are drawn on top of labels
After detecting overlaps with the validation script, I thought "if I add a white background to labels, they'll be readable even over arrows," and added labelBackgroundColor=#FFFFFF to the style of all icons.
fontColor=#999999;labelBackgroundColor=#FFFFFF;shape=image;...
However, this had no effect. When I actually exported to PNG in Draw.io, arrows were still being drawn on top of labels.
Cause: mxGraph edge drawing order
After creating several test files and investigating, I discovered a specification of mxGraph, the underlying library of Draw.io:
Edges (arrows) are always drawn on top of vertices (icons/labels), regardless of XML order or layer
This is a design decision in mxGraph and there is no workaround. Even if you set labelBackgroundColor, change the XML order, or separate into different layers, edges are always drawn at the front.
Workaround: Avoid label areas with waypoints
The only solution is to route the arrow around label areas.
<!-- Before: Arrow crosses the chatbot's label -->
<mxCell id="edge-4" source="svc-cf-bot" target="svc-firestore" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="377" y="401" /> <!-- Passes through label area -->
</Array>
</mxGeometry>
</mxCell>
<!-- After: Route through x=420 to avoid label right edge (x=409) -->
<mxCell id="edge-4" source="svc-cf-bot" target="svc-firestore" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="420" y="401" /> <!-- Outside label area -->
</Array>
</mxGeometry>
</mxCell>
I added this finding to the skill's SKILL.md as a label avoidance rule:
Edges must not pass through icon label text. When an edge would cross a label area, add explicit waypoints to route above the icon or below the label. Target the icon center y for vertical centering when pointing at an icon from the side.
labelBackgroundColor is ineffective as an arrow countermeasure, but since it still helps make labels readable on top of container backgrounds, I continue to use it.
Improvement: Inline edge labels
In the previous skill, I designed step-numbered edges for non-technical user mode:
Flow A: ① ② ③ ④ (white circled numbers)
Flow B: ❶ ❷ ❸ ❹ (black circled numbers)
The intent was to record step number-to-description mappings in the legend, but when actually looking at the diagram, numbers alone don't convey what operation is being performed, requiring a look at the legend each time.
After improvement: Number + description inline
Before: ① → ② → ③
After: ①Send question → ②Knowledge search → ③AI answer generation
Since each arrow directly carries a description, the diagram becomes self-documenting. The legend only needs to indicate the type of flow (e.g., "①–⑤ Chat response flow").
This change in Draw.io XML only requires changing the value attribute of edge elements:
<!-- Before -->
<mxCell id="edge-1" value="①" ... />
<!-- After -->
<mxCell id="edge-1" value="①Send question" ... />
Improvement: Logical grouping in layout
In the layout initially generated by the skill, all services were arranged in a single horizontal line:
Chatbot → Knowledge Search → AI Answer Generation → ... → Log Management
This layout had two problems:
1. Log management was misplaced — It was placed at the end of the flow, making it look like the final step, but it is actually a side effect of bot processing
2. Knowledge search and AI answer generation were discrete — They were arranged in a horizontal line, but these two are actually two steps of a single concept called RAG processing and have a logically close relationship
Improved layout
Chatbot → Log Management Knowledge Search
↓
AI Answer Generation → Conversation History
- Log management moved next to the chatbot — visually clear that it's a side effect
- Knowledge search and AI answer generation stacked vertically — expresses logical cohesion as RAG processing
Reflecting improvements in SKILL.md
I reflected these improvements in the skill's SKILL.md so that the same quality can be achieved in future diagram generation. Main additions:
Visual QA workflow (Step 5)
#### 5a. Automated coordinate validation
uv run python scripts/validate_drawio.py docs/<slug>.drawio \
--export-coords docs/<slug>-coords.json
#### 5b. Visual inspection
/opt/homebrew/bin/drawio -x -f png -s 2 -o docs/<slug>.png docs/<slug>.drawio
#### 5c. Fix and iterate
Three new rules
- Z-order rule: In Draw.io, edges are always drawn on top of vertices. The only solution is to route around label areas using waypoints
- Edge separation rule: Edges sharing the same path should be routed with at least 40px spacing
- Label avoidance rule: Edges must not pass through icon label text areas
Adoption of inline edge labels
### Inline step-numbered edges
- Flow A: ①Send question ②Knowledge search ③AI answer generation(white circled + description)
- **Always include the description inline** — makes edges self-documenting
What I learned
You can't leave "visual quality" entirely to the LLM
LLMs can generate correct XML structure, but they cannot "see" and judge the coordinate relationships between elements. By supplementing with tools like coordinate validation scripts, you can run a generate→validate→fix loop.
Draw.io's rendering order is counterintuitive
The idea that "changing XML order changes z-order" is correct for vertices, but the specification that it has absolutely no effect on edges was not something I understood until I confirmed it with actual test files. It's not documented explicitly either — the kind of knowledge you discover through trial and error.
Skill improvement is a "use it and fix it" cycle
The SKILL.md I initially created covered references to 163 icons and layout rules, but using it on an actual project revealed design insights such as "inline labels are more readable" and "log management should be placed as a side effect, not at the end of the flow." By feeding these back into SKILL.md, the skill gets smarter the more you use it.
Summary
I added automated coordinate validation and design pattern improvements to the Claude Code GCP architecture diagram skill.
- validate_drawio.py (536 lines): Recursive coordinate resolution, label BBox estimation, edge routing approximation, 4 types of overlap detection
- Z-order discovery: Draw.io edges are always drawn on top of vertices → route around label areas using waypoints
- Inline edge labels: Pairing numbers with descriptions like ①Send question makes diagrams self-documenting
- Logical layout grouping: Place related services nearby, and keep side-effect services off the main flow line
In the previous article I made it possible to produce accurate icons just by "copy-pasting style strings," but with this round of improvements, "layout and routing can also be quality-checked automatically." Skills aren't finished once created — by using them in practice and feeding back what you learn, they gradually approach a truly practical level.
