Design principles (all DAG rendering rules)
1. Edges: connect each node to its parent, down first and then right
One edge-routing rule: connect each node to its parent, routing from the parent down to the child’s row, then right to the child. Every edge follows this rule.
- Parent = caller first (the invoking node); use predecessor only when caller is absent.
- ROOT → question: down from ROOT, then right to the question. The question must connect to ROOT.
- Question → reply A and reply A → tool: route down then right, increasing indentation for nested function calls.
- Nodes in the same column (question / B replied) connect with a vertical line from the upper node.
- There is no single line spanning an entire column. Each vertical segment runs from a parent to its child’s row; parent links define the tree without arbitrary connections.
2. Node shape = role
◇ ROOT (white-filled diamond) · ○ user (human message, circle) · △ llm (AI/agent message, triangle) · □ code (tool/function call, square) · ◉ merge (filled circle with a hole).
Messages from another agent use △, not ○; circles are reserved for human users.
3. Color = branch identity, not edge type
Each lane (branch) has one color. Every edge uses the color of the branch it belongs to or targets.Never distinguish edge types by color, which would conflict with branch identity.
4. Line style = edge type, independent of color
- ━ Solid: calls within a branch (parent → child; ordinary chat/nested calls)
- - - Dashed 5 4: retry fork (manual retry/rewrite)
- -·- Dash-dot 4 2 1 2: model-created branch (spawn_branch)
- ··· Dotted 1 5: cross-branch communication (send_to_branch)
- ═ Thick solid 2.4: merge input
- ┄ Long dash 4 4: attach return (child result embedded in parent)
5. Columns (lane / tier)
- tier (indentation): fixed by role. ROOT=0, user=1, llm=2, tools=3; nested calls add 1 per level. Children sit down and right of parents.
- lane (branch column): allocate branches in appearance order 0,1,2, without gaps.
- A fork uses two columns: its own vertical axis plus node column. Place the entire new branch to the right of all content in the originating branch, without overlap.
- Square grid: horizontal and vertical spacing match; children sit diagonally down-right at 45°.
- Compact layout: shift rows upward and columns leftward to fill space released by collapsed nodes.
6. Rows (depth) follow time
Order by global created_at (seq), earlier events above. An existing branch (such as communication target B) appears before the send operation referencing it, rather than appearing newly created at send time. Retry siblings share a row as parallel alternatives.
7. Cross-branch communication (agent ↔ agent) is asynchronous
Synchronous blocking would make A wait for B, serializing parallel branches or causing deadlock. Asynchronous execution allows a background agent to work while conversation continues:
- A’s △ invokes send_to_branch → A gets a □ tool node. The tool returns immediately without blocking A, which continues its own work.
- The message reaches B from another agent → △ “B received,” followed by △ “B responds.”Both nodes belong to branch B (B’s color and connections), not A.
- After B responds, the result is delivered asynchronously to A’s end → △ “B replied.” It is a new externally supplied input turn for A, at the same level as “Question” (user level, attached to A’s main conversation).
- A responds to it → △ “A responds.”
- Two dotted communication edges: A → B and B → A. Neither branch blocks.
8. Branch merges / child-branch work
- Merge: N branches converge on one ◉ merge node with multiple parents. It sits in the base lane; other branches connect with thick merge-input edges.
- Child-branch work: A invokes spawn_branch to create B (dash-dot edge; B starts on the spawn row) → B runs independently → its result returns through an attach edge.
9. Cross-branch reference edges: hidden until endpoint hover, then animated
Call edges (same branch / retry / spawn, defining graph structure) remain visible.
Reference edges (send → received, response → replied, merge inputs, and attach returns: cross-branch references) are hidden by default: reveal an edge only while hovering either endpoint node, with a directional animation (moving dashes). Hide it again when the pointer leaves.
Rationale: many such edges would obscure the graph if always visible. Hover reveals a node’s connections without persistent clutter.
10. What this diagram does
This page uses an executable layout algorithm matching the backend graph_layout layout algorithm is implemented here with matching lane/tier/depth and column-allocation rules. Input node data → compute positions → render. Switch scenarios above to see actual computed layouts for different datasets.