Marrow

How layout is chosen

The six archetypes, the exact rules that select one, the routing and tidy passes, and the geometry linter that gates the build.

You don't pick a layout. Marrow classifies the topology into one of six archetypes, computes a single layout for it, and lints the result. This page gives the actual rules so you can predict — and control — the outcome.

The pipeline

The Marrow Compiler Pipeline

End-to-end deterministic compilation from declarative YAML to self-contained HTML

1. Schema & Validation
packages/schema
Diagnostics

Parses YAML with exact line/column tracking. Validates strictly against schema.

  • •Unknown keys fail with MRW-E005 (x- prefix allowed for private metadata)
  • •Normalizes device coordinates, interfaces, and link bundles into Topology
  • •Did-you-mean fuzzy suggestions for typos and vendor icon IDs

The chosen archetype is shown in every diagram's sidebar under About → Layout, so you can always check what the engine decided.

Classification, in precedence order

Rules are evaluated top to bottom; the first match wins. Declared structure always outranks inferred shape.

The 6 Layout Archetypes

Evaluated top-to-bottom in strict precedence order. First match wins.

Hierarchical Tiered
Rule: Every device declares a tier

Classic enterprise hierarchy: Edge (rank 0) → Core → Distribution → Access → Endpoint.

Engine Placement: Top-down ELK layered alignment with horizontal rank separation

1. zoned — two or more zones contain devices

Each zone is laid out independently and the results are packed into a mosaic. This is the strongest signal available; if you want zones, this is how you get them.

2. tiered — every device declares a tier

ELK layered with tier partitions, edge at the top through endpoint at the bottom. One device missing a tier disqualifies the whole topology.

3. ring — a single cycle

Requires 4+ devices, the graph connected, and every device having exactly two neighbors. Placed on a circle.

Requires 4+ devices. The engine takes the one or two highest-degree devices (each needing degree ≥ 3) as hubs, then demands purity: every non-hub device must connect exclusively to hubs.

A single spoke-to-spoke link disqualifies the entire topology from hub-spoke and drops it to layered. This is the rule that surprises people most. If you want the radial branch-office look, the branches must not talk to each other in the file — even if they do in real life.

Hubs sit at the center; spokes are placed on a ring whose radius is derived from the actual widths of the spoke nodes, so the arrangement is symmetric and overlap-free by construction rather than by a fixed device count.

5. mesh — small and dense

At most 16 devices, edge density ≥ 0.4, average degree ≥ 3. Circular placement with polyline routing.

6. flat — everything else

Plain ELK layered.

Getting the shape you want

You wantDeclare
Buildings / sites side by side≥ 2 zones with devices in them
Classic top-down hierarchytier on every device
Radial WAN / branch view1–2 hubs, spokes touching only hubs
A ringExactly two neighbors per device, connected
A dense fabric≤ 16 devices, well-connected

If the layout is wrong, the description is wrong. Change what you declare, not where things sit.

Routing and the tidy pass

Placement decides where nodes go; routing decides how cables get between them. Marrow routes orthogonally with libavoid, then runs a tidy pass that applies cable-tray discipline — the principle that no line owns a free path:

  • Coincident parallels. Multiple links between the same pair are routed on one visible stroke. A plain parallel run carries an ×N count; when the members share a lag id the run is chipped with that id instead (PO1), since the bundle has a name. Each link keeps its own identity for hover and status.
  • Gutter buses. When a parent feeds three or more children in the row below, the fan-out is rerouted onto a shared lane: parent spine → horizontal lane → taps. Lanes are claimed per gutter with collision guards; any channel that would clip a node falls back to its original route.
  • Draw order. Healthy edges draw first so alarms sit on top of shared runs.
  • Level of detail. Leaf links (endpoint tier) fade at far zoom, so the zoomed-out view reads trunks and structure.

Tidy applies to zoned, tiered, and flat archetypes. Radial shapes keep their spokes.

The effect is large: on a real HQ zone, 165 links collapsed to 36 unique geometries. "Never crosses" is impossible for non-planar graphs — channels are the answer instead.

The geometry linter

The scene is checked before anything is rendered.

LevelMeaningExamples
L1Fails the buildNode overlap on label-inclusive boxes; edge segments crossing foreign node interiors; endpoints not touching their node's box; port chips overlapping
L2Auto-repairedFixable geometry defects
L3AdvisoryExcessive crossings, extreme aspect ratio

An L1 failure is not a warning you can ignore — the build stops. That's deliberate: a diagram with overlapping nodes is worse than no diagram, and the guarantee "output is never overlapping" is only worth something if it's enforced. See ADR-0007.

Density presets and scaling

Marrow sizes node boxes, port chips, typography, and ELK layout spacing using layout metrics presets. Set density in the topology header or under layout.density in marrow.config.yaml to adjust diagram compacteness:

marrow: 2
name: Dense Fabric
density: compact      # compact | standard | spacious (default: standard)
scale: 0.85           # numeric multiplier between 0.4 and 2.5 (default: 1.0)

The layout engine applies density presets across both SVG and Canvas renderers:

Metriccompactstandard (Default)spacious
Icon Chip48 px64 px80 px
Label Font10 px12 px14 px
Label Max Width130 px168 px210 px
ELK Layer Spacing60 px88 px116 px
ELK Node Spacing32 px48 px64 px
Zone Padding32 px48 px64 px
Port Chip Height14 px16 px18 px

The optional scale multiplier linearly multiplies base metric dimensions. For dense data center fabrics with over 100 switches, combine density: compact with scale: 0.75 to reduce footprint. For executive presentation slides and high-resolution displays, use density: spacious with scale: 1.2.

Pinning

pin: { x: 100, y: 200 } fixes a device's position. It exists for the rare case where a physical arrangement genuinely matters — a rack elevation, a geographically meaningful layout.

Treat it as a last resort. Pins bypass the placement engine, so they can produce the overlaps the linter then refuses to build, and they rot the moment the topology changes around them. If you find yourself pinning more than a couple of devices, the structure you declared is probably not the structure you meant.

Legacy x-v1-config

Files brought forward by marrow import may carry an x-v1-config: block with engines:, layouts:, and output_filename:. It is inert. Version 1 rendered one variant per engine × layout combination; v2 has no such mode and never reads the block. Delete it when you see it — it has zero effect and misleads readers into expecting multiple layouts.