Command Palette

Search for a command to run...

Arbtr

Command Palette

Search for a command to run...

The Decision Graph

Core Concept

The graph is Arbtr's core differentiator. It shows decisions as nodes and their relationships as edges.

What is the Graph?

The Decision Graph is a visual representation of your team's architectural decisions and how they relate to each other.

  • Nodes are decisions (e.g., "Use PostgreSQL")
  • Edges are relationships (e.g., "Supabase depends on PostgreSQL")
  • Domains are clusters (e.g., "Database decisions")

Unlike a wiki or document list, the graph shows structure. You can see what depends on what, what conflicts with what, and what happens if something changes.

Reading Nodes

Each node encodes information visually:

Status Colors

Active

Open for discussion

Under Discussion

Active debate

Approved

Concluded: Yes

Rejected

Concluded: No

Deferred

Decided not to decide

Archived

Hidden from view

Importance Badges

Nodes show badges indicating how "load-bearing" a decision is:

ANCHOR

Company-defining

COMMITTED

High-impact

STANDARD

Default weight

TENTATIVE

Experimental

Reading Edges

Edges are colored and styled based on relationship type:

depends_on

B requires A to be true

conflicts_with

A and B are mutually exclusive

supersedes

B replaces A

enables

A makes B possible

constrains

A limits options for B

derived_from

B is based on or related to A

Interactive Guide

The graph includes a built-in guide panel that shows all available controls. Click the book icon in the bottom-right corner of the graph, or press ? to toggle it.

Canvas Controls

ScrollZoom in/out
Drag canvasPan around the graph
Ctrl + scrollZoom faster

Node Controls

ClickSelect node
DragMove node position
Double-clickOpen decision details
Ctrl + clickMulti-select nodes

Node positions are saved automatically to the database.

Edge Controls

Edges support two routing modes: smooth bezier curves (default) and orthogonal step routing (90° angles).

Routing

Double-click edgeConvert to step routing
Shift + dbl-clickReset to smooth curve

Editing Step Routes

Drag handleMove path segment
Double-click edgeAdd new segment
Right-click handleDelete segment

Labels

Drag labelSlide along edge path
Click menu (⋮)Edit relationship

Domain Controls

Click headerSelect domain
Drag headerMove domain
Drag edgesResize domain
Collapse buttonCollapse/expand domain

Drag decisions into domains to group them visually.

Keyboard Shortcuts

Cmd/Ctrl + KOpen command palette
EscapeDeselect / close panels
DeleteDelete selected (with confirm)
Cmd/Ctrl + ZUndo last action
?Toggle guide panel

Selection

ClickSelect single item
Ctrl/Cmd + clickAdd to selection
Drag boxSelect multiple items
Click canvasClear selection

Graph Linting

Arbtr automatically detects structural problems in your decision graph:

Circular Dependencies

A depends on B depends on C depends on A. Something is wrong.

Conflict + Dependency

A conflicts with B, but C depends on both. Impossible state.

Time Travel Paradox

A supersedes B, but A was created before B. Check the timeline.

i
Linting errors appear as warnings on edges. Click them to see details and suggested fixes.

Technical Implementation

Rendering

The graph is rendered using React Flow with custom node and edge components. Layout positions are persisted per-user per-team to graph_layouts.

  • Initial layout: Dagre algorithm for hierarchical positioning
  • Manual overrides saved to database on drag end
  • Domain boundaries calculated from child node positions
  • Edge routing: Bezier (default) or orthogonal step paths

Linting Algorithm

Graph linting runs client-side on every state change. It detects:

Lint rules
// Circular dependency detection
// Uses DFS with visited set - O(V+E)
function detectCycles(graph: Graph): DecisionId[][] {
  // Returns array of cycle paths
}

// Conflict + dependency paradox
// If A conflicts_with B, nothing should depend on both
function detectConflictDependency(graph: Graph): LintWarning[] {
  // Returns warnings with affected decisions
}

// Time travel paradox
// supersedes edges should flow from newer → older decisions
function detectTimeTravel(graph: Graph): LintWarning[] {
  // Compares created_at timestamps
}

Performance

The graph renders interactively up to ~500 nodes. Beyond that, consider:

  • Filtering by domain to reduce visible nodes
  • Archiving old decisions to remove from graph
  • Collapsing domains to reduce rendering complexity

Data Model

Complete interfaces for graph nodes, edges, and domains.

lib/types.ts
interface GraphNode {
  id: string
  title: string
  slug: string
  status: DecisionStatus
  importanceLevel?: DecisionImportanceLevel
  domainId?: string
  tags: string[]

  // Computed counts for visual indicators
  dependencyCount: number    // Outgoing depends_on edges
  conflictCount: number      // conflicts_with edges
  relationshipCount: number  // Total edges

  // Scenario support (ghost nodes)
  scenarioId?: string        // NULL = live, UUID = ghost
  isGhost: boolean

  // Position (persisted per-user)
  position?: { x: number; y: number }
}

interface GraphEdge {
  id: string
  source: string            // Source decision ID
  target: string            // Target decision ID
  type: RelationshipType
  note?: string             // Optional annotation
  createdBy?: string        // Who created this relationship
  createdAt: Date

  // Routing data (persisted)
  routingMode: 'smooth' | 'step'
  stepPath?: { x: number; y: number }[]
  labelPosition?: number    // 0-1 along path
}

type RelationshipType =
  | "depends_on"      // Target requires source
  | "conflicts_with"  // Mutually exclusive (symmetric)
  | "supersedes"      // Target replaces source
  | "enables"         // Source makes target possible
  | "constrains"      // Source limits target's options
  | "derived_from"    // Target is based on source

interface GraphDomain {
  id: string
  name: string
  color: string
  description?: string

  // Computed bounds from child nodes
  bounds?: { x: number; y: number; width: number; height: number }
  isCollapsed: boolean
  nodeCount: number
}
i
Database tables: decisions, decision_relationships,domains, graph_layouts
    The Graph | Arbtr Docs