Add drawio-main skill (renamed from drawio-main-v1)
Draw.io diagram generation and analysis skill: mandatory layout and style rules, XML reference, negative-space companion diagrams, and the drawio-tools TypeScript CLI with 12 analysis/validation actions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
# drawio-main — Layout Rules
|
||||
|
||||
Connector placement and layout rules for draw.io diagrams generated by this skill.
|
||||
These rules are **MANDATORY** — apply them when generating any diagram.
|
||||
|
||||
---
|
||||
|
||||
## General layout rules — MANDATORY
|
||||
|
||||
- Use a **10pt grid**; align all shapes to grid increments
|
||||
- Leave ≥ 40px horizontal and ≥ 40px vertical gaps between shape columns/rows for connector corridors
|
||||
- Keep connector labels short; place them in clear corridors, never overlapping shapes or other labels
|
||||
- Before finalising, verify: no shape overlap, no connector-shape overlap, no label overlap, no page clipping
|
||||
- **Universal 40pt spacing rule** — every shape must have at least 40pt of clear space on **all four sides**:
|
||||
- **From parent header bottom**: child `y = startSize + 40` (measured from top of swimlane including header)
|
||||
- **From parent sides**: child `x ≥ 40` from the left/right inner border of the swimlane
|
||||
- **From parent bottom**: bottom of last child row + 40 = swimlane total height
|
||||
- **Between siblings** (same nesting level): gap between any two adjacent shapes (horizontal or vertical) = 40pt exactly — all sibling gaps must be identical
|
||||
|
||||
Swimlane height formulas:
|
||||
- Single row: `total_height = startSize + 40 + child_height + 40`
|
||||
- Two rows: `total_height = startSize + 40 + row1_h + 40 + row2_h + 40`
|
||||
- Round up total to the nearest multiple of 40 if needed; absorb any remainder into the bottom gap (≥ 40)
|
||||
|
||||
Examples:
|
||||
- `startSize=40`, 1 row h=40: total = 40+40+40+40 = 160 (children at y=80, children w=160 with x-gap=40 between them)
|
||||
- `startSize=40`, 1 row h=80: total = 40+40+80+40 = 200 (children at y=80)
|
||||
- `startSize=40`, 2 rows h=40 each: total = 40+40+40+40+40+40 = 240 (row1 at y=80, row2 at y=160)
|
||||
|
||||
After generating or editing a diagram, run `page-spacing-audit` and `page-swimlane-audit` to verify.
|
||||
|
||||
---
|
||||
|
||||
## Diagram Layers and Connector Rules — MANDATORY
|
||||
|
||||
### Layer model
|
||||
|
||||
| Layer | Contents | Connector may overlap? |
|
||||
|---|---|---|
|
||||
| **0 — Background / containers** | Swimlanes, zone boundaries, grouping rectangles | **YES** — connectors may pass through containers |
|
||||
| **1 — Primary shapes** | Core nodes: main system boxes, components, use cases | **NEVER** |
|
||||
| **2 — Actor shapes** | Human actors (stick figures), external IT systems | **NEVER** |
|
||||
| **3 — Connectors** | Edges, waypoints, routing corridors | **AVOID** — crossing other connectors is a last resort |
|
||||
| **4 — Labels / annotations** | Edge labels, callouts, legend text, title blocks | **NEVER** |
|
||||
|
||||
> **Core rule: a connector may only overlap Layer 0. It must NEVER overlap Layers 1, 2, or 4.**
|
||||
|
||||
### Label-crossing prohibition — MANDATORY
|
||||
|
||||
**A connector must NEVER pass through the textual label of any shape — regardless of which layer the shape belongs to.**
|
||||
|
||||
- Container shapes (Layer 0 / swimlanes) have a header bar at the top that contains the label. Connectors must not pass through this header area
|
||||
- For swimlanes with `startSize=40`, the header occupies `y` to `y+40`. Any connector that enters or exits vertically through the header is forbidden; route it through the side or bottom instead
|
||||
- Leaf shapes (Layer 1/2) also carry labels — their entire bounding box is off-limits (existing rule), so this adds no extra constraint for those
|
||||
- To check: identify every shape whose label bounding box (header bar for containers, full box for leaves) intersects a connector segment. Reroute any that does
|
||||
|
||||
### Corner-cutting prohibition — MANDATORY
|
||||
|
||||
**A connector must NEVER route across the corner of any shape — regardless of which layer the shape belongs to.**
|
||||
|
||||
A corner cut occurs when an orthogonal connector exits from one face of a shape and its first waypoint is positioned diagonally (same quadrant as the corner) relative to that exit point, causing the L-shaped segment to visually "clip" the corner region. This is forbidden even when the corner is technically outside the shape bounding box.
|
||||
|
||||
Rule: after choosing the exit face and direction of travel, the first waypoint must be placed such that the initial segment travels **parallel to or away from** the corner edge, not at an angle toward it.
|
||||
|
||||
| Exit face | First segment must go | Forbidden direction |
|
||||
|---|---|---|
|
||||
| Bottom | Down (↓) or laterally clear | Up (↑) toward corner |
|
||||
| Top | Up (↑) or laterally clear | Down (↓) toward corner |
|
||||
| Left | Left (←) or clear | Right (→) toward corner |
|
||||
| Right | Right (→) or clear | Left (←) toward corner |
|
||||
|
||||
### Prefer-downward routing — MANDATORY
|
||||
|
||||
**When a connector can reach its target by routing either upward or downward, always prefer the downward route.**
|
||||
|
||||
Routing upward is only permitted when:
|
||||
- The target is physically above the source (the upward direction is the natural one), OR
|
||||
- Routing downward would create a connector-shape overlap that cannot otherwise be avoided
|
||||
|
||||
Violation example: a connector exits the bottom of a source shape, immediately routes **up** through the source's parent container header, travels horizontally, then comes back **down** — this is forbidden. The first segment after exiting the bottom face must go **down**, not up.
|
||||
|
||||
Decision rule:
|
||||
1. Identify whether target `y_center` is above or below source `y_center`
|
||||
2. If below (or same level): exit source bottom or side → route **downward** → enter target top or side
|
||||
3. If above: exit source top or side → route **upward** → enter target bottom or side
|
||||
4. If the natural downward path is blocked, use a side-exit (left or right) corridor to bypass, then continue downward — do NOT reverse direction to go upward
|
||||
|
||||
### Header-edge prohibition — MANDATORY
|
||||
|
||||
**A connector must NEVER run along (or within 1px of) the bottom edge of a swimlane/container header bar.**
|
||||
|
||||
The header bar of a swimlane occupies the band `[shape.y .. shape.y + startSize]`. Its **bottom edge** is the line `y = shape.y + startSize`. Connectors that travel horizontally across this line are visually indistinguishable from the header border and create a confusing, cluttered diagram.
|
||||
|
||||
- For a swimlane with `startSize=40`, the forbidden horizontal band is within 1px of `shape.y + 40`
|
||||
- Connectors entering or exiting the swimlane via a **vertical segment that straddles** the header-bottom edge are also forbidden
|
||||
- **Fix:** reroute the horizontal segment to travel either:
|
||||
- **Inside** the swimlane body (below `shape.y + startSize + TOL`), or
|
||||
- **Outside / above** the swimlane entirely (above `shape.y - TOL`), or
|
||||
- Via a side corridor that avoids the header band altogether
|
||||
|
||||
**Detection:** `page-connectors-validation` action reports `headerEdgeViolations` — always run it before finishing any diagram that contains swimlanes or container shapes.
|
||||
|
||||
| Situation | Fix |
|
||||
|---|---|
|
||||
| Horizontal segment at `y ≈ container.y + startSize` | Move segment down into the swimlane body or up above the container |
|
||||
| Vertical segment straddles header-bottom edge from inside | Re-enter the swimlane via left or right face instead |
|
||||
| Multiple connectors fanning across the Data Layer header | Use a horizontal corridor at `y = container.y + startSize + 40` (center of the 40pt gap) or route via side bypass |
|
||||
|
||||
### Connector-crossing avoidance — MANDATORY
|
||||
|
||||
**Connectors must not cross each other.** Crossing another connector is a last resort — acceptable only when no feasible reroute exists. When a crossing is unavoidable, it must be a clean 90° crossing. Connectors crossing other connectors is always preferable to a connector crossing a shape label or shape corner.
|
||||
|
||||
| Cause | Fix |
|
||||
|---|---|
|
||||
| Multiple left-side sources fan into one target | Route through a shared left-side vertical corridor; stagger y-entry points |
|
||||
| Sources on both sides of a central shape | Use separate left-corridor and right-corridor |
|
||||
| Sources in different rows heading to the same column | Give each source its own horizontal lane in the corridor |
|
||||
| Long connector crosses shorter ones | Route long connectors through Y_TOP (above all shapes) |
|
||||
|
||||
**Crossing-free strategy:**
|
||||
1. Identify source groups and target groups
|
||||
2. Assign each source group its own corridor lane
|
||||
3. Route connectors in each group in parallel through their lane
|
||||
4. Trace every connector pair — if any two intersect at a non-edge point, redesign
|
||||
|
||||
If a crossing truly cannot be avoided, prefer a right-angle (90°) crossing over an oblique one.
|
||||
|
||||
### Minimal waypoints rule — MANDATORY
|
||||
|
||||
**Add waypoints only when the direct path would clip a Layer 1/2/4 shape.**
|
||||
|
||||
| Situation | Waypoints? |
|
||||
|---|---|
|
||||
| Source and target at same horizontal level, path is clear | **NO** |
|
||||
| Adjacent columns with clear gap between them | **NO** |
|
||||
| Source mid_y falls within target's y-range, path is clear | **NO** |
|
||||
| Path would clip an intermediate shape | **YES** — minimum waypoints to route around it |
|
||||
| Fan-out from a central shape to a stacked column | **YES** — use Pattern E |
|
||||
|
||||
**Before adding a waypoint, ask:** *"Would the direct auto-routed path clip any Layer 1/2/4 shape?"* If no — no waypoints. If yes — first try repositioning shapes (layout-first principle).
|
||||
|
||||
**Keep all connectors the same edge style** (`edgeStyle=orthogonalEdgeStyle`). Never change edge style to work around a layout problem — fix the layout instead.
|
||||
|
||||
### Layout-first principle
|
||||
|
||||
**Redesign the layout to eliminate waypoints before adding them.**
|
||||
|
||||
| Problem | Layout fix |
|
||||
|---|---|
|
||||
| Actor above target's y-range | Move target up, or tighten actor spacing so target mid_y aligns |
|
||||
| Actor below target's y-range | Move target down, or tighten actor spacing |
|
||||
| Fan-out column spans much more than source | Centre source vertically on the column |
|
||||
|
||||
**Shape size is driven by content, not routing** — typically 80–160px tall. If only 1–2 actors fall outside the target's y-range, accept 2 minimal waypoints per connector rather than inflating the shape. Space actors 80–120px apart (centre-to-centre) to keep groups compact.
|
||||
|
||||
### When auto-routing is forbidden
|
||||
|
||||
Never rely on `edgeStyle=orthogonalEdgeStyle` without explicit waypoints when the path passes through intermediate shapes. Use waypoints only where needed — the goal is the minimum number that achieves zero overlaps and zero crossings.
|
||||
|
||||
---
|
||||
|
||||
## Connector Routing Patterns
|
||||
|
||||
### Corridors
|
||||
|
||||
Before placing connectors, identify clear **corridors** — bands free of all shapes.
|
||||
|
||||
**Vertical corridor** between two columns:
|
||||
```
|
||||
GAP_x = (right_edge_of_left_column + left_edge_of_right_column) / 2
|
||||
```
|
||||
|
||||
**Horizontal corridor** between two rows:
|
||||
```
|
||||
GAP_y = (bottom_edge_of_upper_row + top_edge_of_lower_row) / 2
|
||||
```
|
||||
|
||||
Reserve a **top corridor** (`Y_TOP`) above all shapes and a **bottom corridor** (`Y_BELOW`) below all shapes for cross-diagram connectors.
|
||||
|
||||
### Absolute coordinates
|
||||
|
||||
Connector waypoints are canvas-absolute. For nested containers, sum all ancestor offsets:
|
||||
```
|
||||
abs_x = shape.x + parent.x + grandparent.x + ...
|
||||
abs_y = shape.y + parent.y + grandparent.y + ...
|
||||
```
|
||||
|
||||
### Overlap verification (Python)
|
||||
|
||||
```python
|
||||
TOL = 2
|
||||
|
||||
def segment_overlaps_shape(seg, shape):
|
||||
sx1, sy1, sx2, sy2 = shape
|
||||
if seg['type'] == 'H':
|
||||
Y, xA, xB = seg['y'], min(seg['x1'],seg['x2']), max(seg['x1'],seg['x2'])
|
||||
if sy1+TOL < Y < sy2-TOL and max(xA,sx1+TOL) < min(xB,sx2-TOL):
|
||||
return True
|
||||
elif seg['type'] == 'V':
|
||||
X, yA, yB = seg['x'], min(seg['y1'],seg['y2']), max(seg['y1'],seg['y2'])
|
||||
if sx1+TOL < X < sx2-TOL and max(yA,sy1+TOL) < min(yB,sy2-TOL):
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
Exclude the terminal shape (source/target) when checking first/last segments.
|
||||
|
||||
### Pattern A — Top highway
|
||||
|
||||
Long connectors that span the full diagram width:
|
||||
```
|
||||
source → V up to Y_TOP → H across → V down to target
|
||||
```
|
||||
|
||||
### Pattern B — Stacked column approach
|
||||
|
||||
Multiple targets stacked vertically in a column — never route through the column. Approach each from a corridor to the side:
|
||||
```
|
||||
source → V to Y_TOP → H to GAP_x (beside column) → V to target_mid_y → H into target
|
||||
```
|
||||
|
||||
### Pattern C — Narrow gap corridor
|
||||
|
||||
When two adjacent shapes leave a small gap (≥ 10px), that gap is a usable vertical corridor:
|
||||
```
|
||||
corridor_x = (shape_A_right + shape_B_left) / 2
|
||||
```
|
||||
|
||||
### Pattern D — Bypass around a dense row
|
||||
|
||||
When a row of shapes blocks a path to shapes below, go around the right edge:
|
||||
```
|
||||
source → H right to RIGHT_BYPASS → V to target_y → H left to target
|
||||
RIGHT_BYPASS = (rightmost_shape_right + next_column_left) / 2
|
||||
```
|
||||
|
||||
### Pattern E — Fan-out from a central shape to a stacked column
|
||||
|
||||
The most common overlap scenario: one central shape connects to N stacked targets on one side.
|
||||
|
||||
**Solution — vertical fan-out corridor:**
|
||||
1. `GAP = (central_right + column_left) / 2`
|
||||
2. Exit central shape → H to GAP → V to target_mid_y → H into target
|
||||
|
||||
```xml
|
||||
<Array as="points">
|
||||
<mxPoint x="{GAP}" y="{source_mid_y}"/>
|
||||
<mxPoint x="{GAP}" y="{target_mid_y}"/>
|
||||
</Array>
|
||||
```
|
||||
|
||||
**Example** — source mid_y=200, GAP=820, 5 stacked targets:
|
||||
|
||||
| Target | mid_y | Waypoints |
|
||||
|---|---|---|
|
||||
| Item A | 80 | (820, 200) → (820, 80) |
|
||||
| Item B | 200 | direct (no waypoints needed) |
|
||||
| Item C | 320 | (820, 200) → (820, 320) |
|
||||
| Item D | 440 | (820, 200) → (820, 440) |
|
||||
| Item E | 560 | (820, 200) → (820, 560) |
|
||||
|
||||
Constraint: `central_right < GAP < column_left`. If gap < 40px, move the column right.
|
||||
|
||||
### Waypoint XML format
|
||||
|
||||
```xml
|
||||
<mxCell id="conn-1" value="label" style="edgeStyle=orthogonalEdgeStyle;html=1;" edge="1" source="A" target="B" parent="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="450" y="40"/>
|
||||
<mxPoint x="820" y="40"/>
|
||||
<mxPoint x="820" y="320"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Waypoints are canvas-absolute
|
||||
- Edges are children of root layer (`parent="1"`), never of a container
|
||||
- Last waypoint stops at the corridor boundary — draw.io completes the final stub
|
||||
|
||||
### Layout checklist
|
||||
|
||||
- [ ] ≥ 40px gaps between columns and rows (universal 40pt spacing rule)
|
||||
- [ ] Y_TOP corridor reserved above all shapes
|
||||
- [ ] Y_BELOW corridor reserved below all shapes
|
||||
- [ ] Corridor bands contain no shapes
|
||||
- [ ] Python overlap verification run; all overlaps fixed
|
||||
- [ ] Endpoint-touch exclusion applied for first/last segments
|
||||
- [ ] Every connector pair traced — no path intersections at non-edge points
|
||||
- [ ] All connector labels in clear corridors, not overlapping shapes or other labels
|
||||
- [ ] No connector passes through any shape's text label (including swimlane header bars)
|
||||
- [ ] No connector cuts across a shape corner (first segment after exit travels away from corners)
|
||||
- [ ] No connector runs along (or within 1px of) the bottom edge of any swimlane/container header bar (`page-connectors-validation` reports `headerEdgeViolations`)
|
||||
- [ ] All connectors route downward when target is below source; upward only when target is above source
|
||||
|
||||
---
|
||||
|
||||
## Swimlane / Multi-Zone Diagram Routing
|
||||
|
||||
When a diagram uses **swimlane zones** (horizontal bands, each a `swimlane` container), apply the following rules in addition to all rules above.
|
||||
|
||||
### Zone layout reference
|
||||
|
||||
For a typical layered architecture with `startSize=40` swimlanes and 40pt spacing:
|
||||
|
||||
```
|
||||
zone.y ← swimlane top edge
|
||||
zone.y + 40 ← header bottom (label occupies y..y+40)
|
||||
zone.y + 40 + 40 = zone.y+80 ← first child row top (rel y=80 inside container)
|
||||
zone.y + 80 + child_h ← first child row bottom
|
||||
...
|
||||
zone.y + height ← swimlane bottom edge
|
||||
```
|
||||
|
||||
Inter-zone gaps (between adjacent swimlanes) are clean horizontal corridors — use their midpoint as the H-travel y-coordinate for connectors crossing zone boundaries.
|
||||
|
||||
### Bypass corridors
|
||||
|
||||
Always reserve **two vertical bypass corridors** outside all zones:
|
||||
|
||||
| Corridor | x value | Rule |
|
||||
|---|---|---|
|
||||
| LEFT bypass | `zone.x - 20` (e.g. x=60 when zones start at x=80) | All leftward cross-zone connectors |
|
||||
| RIGHT bypass | `zone.x + zone.width + 20` (e.g. x=1260 when zones end at x=1240) | All rightward cross-zone connectors |
|
||||
|
||||
These bypass corridors run the full canvas height and are free of all shapes. **Every connector that must travel between zones should route through one of these corridors.**
|
||||
|
||||
### Multi-row swimlane: horizontal segment placement
|
||||
|
||||
When a swimlane has **two rows of shapes** (row1 and row2), each row has a y-range. Never place a connector H segment at a y-value that falls inside a row's y-range — that will overlap sibling shapes.
|
||||
|
||||
Use only these safe H corridors inside a multi-row services swimlane:
|
||||
|
||||
| Corridor | y value | When to use |
|
||||
|---|---|---|
|
||||
| Inter-row gap | `row1_bottom + (row2_top - row1_bottom)/2` | H travel between row1 and row2 shapes |
|
||||
| Services-bottom gap | `row2_bottom + (zone_bottom - row2_bottom)/2` | H travel below row2, still inside zone |
|
||||
| Inter-zone gaps | midpoint of gap between adjacent zones | H travel outside zones |
|
||||
|
||||
**Example** — services-zone: startSize=40, zone.y=500, row1 abs y=580..620, row2 abs y=660..700, zone bottom=740:
|
||||
- Inter-row gap corridor: **y=640** (midpoint of y=620..660)
|
||||
- Services-bottom corridor: **y=720** (midpoint of y=700..740)
|
||||
|
||||
### Connector routing rules for multi-row swimlanes
|
||||
|
||||
**Rule 1 — Never route H segments through a row's y-range.**
|
||||
If a connector must travel horizontally through the services zone, use y=640 (inter-row) or y=720 (services-bottom), never y=580..620 or y=660..700.
|
||||
|
||||
**Rule 2 — When exiting a shape in row2 that has sibling shapes to its right in the same row:**
|
||||
Do NOT exit from the bottom of the shape and then travel H at y=720 (services-bottom) to the right — this H will cross the V stubs of right-side siblings that exit to the same corridor.
|
||||
|
||||
**Fix options:**
|
||||
- a) Exit from the **right side** of the shape → immediately go to RIGHT bypass x=1260 → V down/up to target (no H inside zone)
|
||||
- b) If right-side exit H would cross a sibling shape body: exit right → first waypoint in the **column gap** to the right (e.g. x=1060 for col5/col6 gap) → V down to services-bottom corridor y=720 → H right to RIGHT bypass → V to target
|
||||
|
||||
**Rule 3 — Left bypass for connectors going downward to lower zones.**
|
||||
When a row1 or row2 shape must connect to a shape in a lower zone (messaging, data), route:
|
||||
```
|
||||
shape_bottom → inter-row corridor y=640 → H LEFT to x=LEFT_BYPASS → V down to target zone corridor → H right to target
|
||||
```
|
||||
This ensures the H segment travels in the clear inter-row gap corridor and never crosses sibling shapes.
|
||||
|
||||
**Rule 4 — connectorShapeOverlaps with zone containers are structural and unavoidable.**
|
||||
Any connector crossing a swimlane zone boundary will be flagged as `connector_shape_overlap` with the zone container. This is expected and acceptable — focus on eliminating `connectorCrossings` (two connectors intersecting each other) and overlaps with **non-container leaf shapes**.
|
||||
|
||||
### Pattern F — Multi-row swimlane fan-out (api-gateway → many services)
|
||||
|
||||
When a shape in an upper zone connects to N shapes spread across multiple columns of a multi-row services swimlane:
|
||||
|
||||
```
|
||||
api-gateway_bottom → H to LEFT_BYPASS at inter-zone gap y → V down LEFT_BYPASS →
|
||||
branch per target:
|
||||
- col1 (leftmost): H right from LEFT_BYPASS to target_center_x at inter-row gap y
|
||||
- col2..colN: same pattern, extending H further right
|
||||
- for row2 targets: V from inter-row corridor down to row2 top, enter from top
|
||||
```
|
||||
|
||||
All H branches travel at the same y (inter-row gap) — they are **parallel**, not crossing.
|
||||
|
||||
### Pattern G — Event bus → services (right bypass fan-in)
|
||||
|
||||
When an event bus (bottom of diagram) connects back up to multiple services:
|
||||
|
||||
```
|
||||
event-bus_right → H right to RIGHT_BYPASS → V up →
|
||||
branch per target:
|
||||
- service in row1: H left from RIGHT_BYPASS at row1_center_y to target
|
||||
- service in row2: stop at services-bottom corridor y=720 → H left → V up 20pt to target bottom
|
||||
- stagger y values slightly (e.g. y=720 for one, y=730 for another) to avoid parallel confusion
|
||||
```
|
||||
|
||||
**Critical:** The H segments going LEFT from RIGHT_BYPASS at y=720/730 will cross V stubs of row2 services that exit their bottoms and route DOWN and RIGHT to the right bypass. To avoid these crossings:
|
||||
- Route those downward connectors via the **LEFT bypass** instead (Pattern F inverse)
|
||||
- OR ensure the rightward services exit via their **right side** (not bottom), so no V stub exists at y=720..730
|
||||
|
||||
### Swimlane validation workflow
|
||||
|
||||
1. Run `page-connectors-validation` after every edit
|
||||
2. Fix `connectorCrossings` first — these are always avoidable
|
||||
3. Fix `connectorShapeOverlaps` with **leaf shapes** (non-containers) — these are overlaps with actual service boxes
|
||||
4. Accept `connectorShapeOverlaps` with zone containers as structural (unavoidable)
|
||||
5. Fix `cornerPortViolations`, `headerEdgeViolations`, `singlePortViolations` — all should reach 0
|
||||
6. Target: `connectorCrossings=0`, `cornerPortViolations=0`, `headerEdgeViolations=0`, `singlePortViolations=0`
|
||||
|
||||
---
|
||||
|
||||
## Sequence Diagram Layout Rules — MANDATORY
|
||||
|
||||
Sequence diagrams have a fundamentally different structure to architecture diagrams. The rules below override or supplement the general connector rules **for sequence diagrams only**.
|
||||
|
||||
### Participants (lifeline headers)
|
||||
|
||||
- **Participant boxes** are rectangular shapes (`rounded=0`) placed in a single horizontal row at the top of the diagram
|
||||
- **HUMAN_ACTOR participants** use `shape=actor`, `width=40`, `height=40` — same as all actor shapes
|
||||
- **Service/system participants** use `rounded=0`, `width=120`, `height=80` (both divisible by 40)
|
||||
- **Participant spacing**: gap between adjacent participant boxes must be ≥ 40pt and divisible by 40. Preferred gap = 80pt, giving a step of `width + 80` between participant x-origins
|
||||
- **Participant x-origin**: must be on the 10pt grid; preferred to be on the 40pt grid
|
||||
- **All participant boxes must have the same height** (uniform row)
|
||||
|
||||
### Lifelines
|
||||
|
||||
- Each participant has exactly one **lifeline** — a vertical dashed edge (`dashed=1; endArrow=none`) centered on the participant
|
||||
- Lifeline x-coordinate = `participant.x + participant.width / 2` (center of participant)
|
||||
- Lifeline starts at `y = participant.y + participant.height` (bottom edge of participant box) and ends above the legend/footer area. Stop lifelines at least 40pt above the legend box top edge
|
||||
- Lifeline color matches the participant stroke color
|
||||
- **Lifelines are Layer-0 elements** — they are background structural elements, equivalent to swimlane containers. Connectors (message arrows) may cross lifelines freely — this is the defining visual characteristic of a sequence diagram
|
||||
|
||||
### Message arrows
|
||||
|
||||
- Message arrows are horizontal edges (`endArrow=block; endFill=1`) drawn at exact y-coordinates, traveling between lifeline x-positions
|
||||
- **Each message occupies its own y-row** — messages must never share the same y-coordinate
|
||||
- **Vertical step between messages**: minimum 20pt, preferred 20–40pt to ensure labels don't overlap
|
||||
- **Message arrow y-coordinates** must be on the 10pt grid
|
||||
- **Label placement**: edge label is above the arrow line; keep it short (≤ 30 chars) to avoid overlap with lifelines it crosses
|
||||
- **Return messages** (response arrows) travel in the opposite horizontal direction; they are drawn 20pt below the corresponding request arrow
|
||||
|
||||
### Step / phase labels
|
||||
|
||||
- Phase separator labels ("1. Search & Browse", etc.) are text shapes (`style=text`) positioned in a **left margin column** that must not overlap any lifeline
|
||||
- Left margin column: `x = 20`, `width ≤ participant_leftmost_x - 20 - 4` (at least 4pt clear gap from the leftmost lifeline)
|
||||
- Phase labels are placed at the y-coordinate of the first message in that phase, minus 20pt
|
||||
|
||||
### Lifeline × message crossing — STRUCTURAL EXEMPTION
|
||||
|
||||
**Lifeline × message arrow crossings are structural and expected in sequence diagrams.** They must NOT be treated as violations. The `page-connectors-validation` tool will report these as `connectorCrossings` — accept them without fix.
|
||||
|
||||
Specifically:
|
||||
- A vertical lifeline edge crossing a horizontal message arrow edge = **structural, expected, acceptable**
|
||||
- Only report as a true crossing violation: two message arrows crossing each other (both horizontal) or two lifelines crossing each other (impossible by construction)
|
||||
|
||||
This exemption applies only to lifeline edges (identified by: `dashed=1; endArrow=none; vertical segment spanning many messages`). All other crossing rules remain fully in force.
|
||||
|
||||
### Legend container — STRUCTURAL EXEMPTION
|
||||
|
||||
Legend sample arrows (short colored stubs showing connector styles) placed inside a plain container shape will be reported as `connectorShapeOverlaps` with the container. This is structural and expected — the legend connectors are intentionally drawn inside the container boundary.
|
||||
|
||||
Accept `connectorShapeOverlaps` where:
|
||||
- The overlapping edge is a legend sample stub (short, non-connected edge with `parent=legend-box`)
|
||||
- The overlapping shape is the legend container itself (`id=legend-box`)
|
||||
|
||||
All other `connectorShapeOverlaps` with non-container leaf shapes must still be fixed.
|
||||
|
||||
### Sequence diagram validation targets
|
||||
|
||||
| Metric | Target | Notes |
|
||||
|---|---|---|
|
||||
| `connectorCrossings` (lifeline × message) | **Accepted as structural** | Expected behavior of sequence diagrams |
|
||||
| `connectorCrossings` (message × message) | **0** | Two horizontal messages must never cross |
|
||||
| `connectorShapeOverlaps` (legend stubs × legend box) | **Accepted as structural** | Legend connectors inside their container |
|
||||
| `connectorShapeOverlaps` (message × participant box) | **0** | Message arrows must not pass through participant boxes |
|
||||
| `connectorShapeOverlaps` (lifeline × step-label shape) | **0** | Step labels must be in a clear left margin column |
|
||||
| `cornerPortViolations` | **0** | |
|
||||
| `headerEdgeViolations` | **0** | |
|
||||
| `singlePortViolations` | **0** | |
|
||||
| `overlappingPairs` (shape bbox) | **0** | No shape bounding boxes overlap |
|
||||
|
||||
### Sequence diagram layout checklist
|
||||
|
||||
- [ ] All participants in one horizontal row, uniform height, gaps divisible by 40
|
||||
- [ ] HUMAN_ACTOR participants: `shape=actor`, width=40, height=40
|
||||
- [ ] Service participants: `rounded=0`, width=120, height=80
|
||||
- [ ] Lifelines centered on participants, stop 40pt above legend
|
||||
- [ ] Lifelines use `dashed=1; endArrow=none`; color matches participant stroke
|
||||
- [ ] Each message at a unique y-coordinate, on 10pt grid, min 20pt apart
|
||||
- [ ] Step/phase labels in left margin column (x=20, clear of all lifelines)
|
||||
- [ ] No message × message crossings (horizontal × horizontal)
|
||||
- [ ] No message overlapping a participant box
|
||||
- [ ] Legend items are children of a plain container (not swimlane)
|
||||
- [ ] `page-shape-bbox-validation` reports `overlappingPairs=0`
|
||||
- [ ] `page-connectors-validation` reports `cornerPortViolations=0`, `headerEdgeViolations=0`, `singlePortViolations=0`
|
||||
Reference in New Issue
Block a user