Campaign Graph View
The campaign graph is the primary view for plan details. It renders log entries as a vertical flow of connected nodes — each primary action (send email, send SMS, ask user, complete) becomes a card with inline approve/reject controls and expandable supporting context.
The timeline view remains available as a secondary tab for audit purposes.
Layout
Section titled “Layout”The plan details view uses a tab switcher with two views:
| Tab | Default | Content |
|---|---|---|
| Graph | ✓ | Visual flow with connected node cards |
| Timeline | Chronological flat list (existing view) |
Graph Data Model
Section titled “Graph Data Model”Three structs represent the graph:
pub struct GraphNode { pub id: Uuid, pub log_entry: PlanLogDisplay, // The primary action pub supporting: Vec<PlanLogDisplay>, // Attached context entries pub position_index: usize, // Display order (0 = newest) pub execution_run: Option<usize>, // Run boundary tracker}
pub struct GraphEdge { pub from: Uuid, // Older node pub to: Uuid, // Newer node pub is_cross_run: bool, // True when edge spans a schedule boundary}
pub struct GraphData { pub nodes: Vec<GraphNode>, // Newest-first pub edges: Vec<GraphEdge>,}Tool Call Roles
Section titled “Tool Call Roles”Every PlanLogEntryToolCall variant is classified into one of three roles that drive graph grouping:
pub enum ToolCallRole { Primary, // Top-level graph nodes PostAction, // Attach to the previous primary node Supporting, // Attach to the next primary node}| Role | Tool Calls | Graph Behavior |
|---|---|---|
Primary | SendEmail, SendSms, AskUser, CompletePlan, FailPlan, ScheduleNextExecution | Becomes a top-level node card |
PostAction | WriteInteractionNote, UpdateSystemNote | Attaches to the previous primary node’s supporting list |
Supporting | GetContactDetails, GetContactNotes, ListPlanContacts, GetConversationHistory | Attaches to the next primary node’s supporting list |
Each variant also provides a display_name() for consistent labeling across the UI (e.g., SendEmail → “Send Email”, AskUser → “Question”).
Grouping Algorithm
Section titled “Grouping Algorithm”build_graph_data() in src/mods/plan/build_graph_data.rs converts a chronologically-sorted log into a GraphData structure:
- Walk entries chronologically, maintaining a
bufferof non-primary entries - Skip
ExecutionStartedandExecutionEndedsystem events (they only track run boundaries) - Track execution runs — increment the run counter when
ExecutionStartedfollows aScheduleNextExecution - On primary entry: partition the buffer into post-action and supporting entries. Post-action entries attach to the previous node; supporting entries attach to the current node
- Trailing buffer: any remaining entries attach to the last primary node
- Reverse nodes to newest-first and assign
position_indexvalues - Build edges between consecutive nodes, marking
is_cross_runwhen execution runs differ
Chronological log: [GetContact] [Reasoning] [SendEmail] [WriteNote] [Schedule] ... [SendSMS] ↓ ↓ ↓ ↓ ↓ ↓ Supporting Supporting Primary PostAction Primary Primary
Graph result (newest-first): SendSMS → Schedule → SendEmail │ │ │ supporting: [GetContact, Reasoning] │ supporting: [WriteNote] (post-action from Schedule) └── cross-run edge (dashed)Components
Section titled “Components”PlanGraphComponent
Section titled “PlanGraphComponent”File: src/mods/plan/components/plan_graph_component.rs
The main container. Calls build_graph_data() on the log entries and renders nodes with edges between them. Handles plan state indicators:
| Plan State | Rendering |
|---|---|
PendingReview | Start node with approve/reject buttons |
Executing | Ghost node with spinner at the top |
StandBy | Badge showing next execution time |
AwaitingInput | Auto-expands the first node requiring input |
Completed | Static graph, no action controls |
Failed | Static graph with failure indicator |
PlanGraphNodeComponent
Section titled “PlanGraphNodeComponent”File: src/mods/plan/components/plan_graph_node_component.rs
Renders a single node card with:
- Status styling — color-coded by tool call output state (pending, done, failed)
- Expand/collapse — click to reveal supporting entries grouped inside the card
- Inline actions — approve/reject buttons for pending actions, answer input for
AskUser - Tool label — uses
display_name()for consistent naming
PlanGraphEdgeComponent
Section titled “PlanGraphEdgeComponent”File: src/mods/plan/components/plan_graph_edge_component.rs
SVG vertical connector between nodes. Renders a solid line for same-run edges and a dashed line with a run boundary label for is_cross_run edges.
Adding a New Tool Call
Section titled “Adding a New Tool Call”When you add a new tool call variant to PlanLogEntryToolCall:
- Add the
display_name()match arm with a human-readable label - Add the
role()match arm — decide if it’sPrimary,PostAction, orSupporting - If it’s
Primary, the graph automatically creates a node for it - If it needs custom rendering in the node card, update
PlanGraphNodeComponent
Key Files
Section titled “Key Files”| File | Purpose |
|---|---|
src/mods/plan/types/plan_graph_type.rs | GraphNode, GraphEdge, GraphData structs |
src/mods/plan/types/plan_log_entry_type.rs | ToolCallRole enum, role(), display_name() |
src/mods/plan/build_graph_data.rs | Grouping algorithm + 10 unit tests |
src/mods/plan/components/plan_graph_component.rs | Main graph container |
src/mods/plan/components/plan_graph_node_component.rs | Node card with actions |
src/mods/plan/components/plan_graph_edge_component.rs | SVG edge connector |
src/mods/plan/views/plan_details_view.rs | Tab switcher (Graph + Timeline) |