Dependencies represent relationships between cards. Kanban CLI supports three types of relationships: blocking dependencies (one card must complete before another can start), parent-child hierarchies (organizational grouping), and general relations (informational links).
pub enum CardEdgeType { /// This card blocks the target (must complete before target can start) /// Enforces DAG - no cycles allowed Blocks, /// General relationship (informational, allows cycles) RelatesTo, /// Organizational grouping - parent contains child /// Enforces DAG - no cycles allowed (can't be own ancestor) ParentOf,}
// Blocks and ParentOf require DAG (no cycles)assert!(CardEdgeType::Blocks.requires_dag());assert!(CardEdgeType::ParentOf.requires_dag());// RelatesTo allows cyclesassert!(CardEdgeType::RelatesTo.allows_cycles());
Attempting to create a cycle with Blocks or ParentOf edges will return a CycleDetected error.
// Card A must complete before Card B can startgraph.add_blocks(card_a, card_b)?;// Prevents cyclesgraph.add_blocks(card_b, card_c)?;graph.add_blocks(card_c, card_a)?; // Error: CycleDetected
// Get all cards blocking a given cardlet blockers = graph.blockers(card_b);assert!(blockers.contains(&card_a));// Get all cards blocked by a given cardlet blocked = graph.blocked_by(card_a);assert!(blocked.contains(&card_b));
// Check if all blocking cards are completelet can_start = graph.can_start(card_c, |id| { cards.iter() .find(|c| c.id == id) .map(|c| c.is_completed()) .unwrap_or(false)});if can_start { println!("Card C ready to start!");}
Use blocking relationships to model technical dependencies: “Can’t deploy until tests pass” or “Can’t implement feature B until API from feature A is ready.”
// Card B becomes a child of Card Agraph.set_parent(card_b, card_a)?;// Multiple children allowedgraph.set_parent(card_c, card_a)?;graph.set_parent(card_d, card_a)?;assert_eq!(graph.children(card_a).len(), 3);
// Get direct childrenlet children = graph.children(parent_id);// Get direct parentslet parents = graph.parents(child_id);// Get all ancestors (parents, grandparents, etc.)let ancestors = graph.ancestors(child_id);// Get all descendants (children, grandchildren, etc.)let descendants = graph.descendants(parent_id);// Count direct children (for [N] badge)let count = graph.child_count(parent_id);
// Create bidirectional relationshipgraph.add_relates_to(card_a, card_b)?;// Both cards see the relationshipassert!(graph.related(card_a).contains(&card_b));assert!(graph.related(card_b).contains(&card_a));// Allows cyclesgraph.add_relates_to(card_b, card_c)?;graph.add_relates_to(card_c, card_a)?; // OK - cycles allowed
Use RelatesTo for informational links like “See also” or “Related to” without enforcing ordering.
// Archive all edges for a card (soft delete)graph.archive_card_edges(card_id);// Archived edges are excluded from queriesassert_eq!(graph.blockers(card_id).len(), 0);// Can be restoredgraph.unarchive_node(card_id);assert_eq!(graph.blockers(card_id).len(), 1);
// Remove all edges permanentlygraph.remove_card_edges(card_id);// All incoming and outgoing edges deletedassert_eq!(graph.blockers(card_id).len(), 0);assert_eq!(graph.blocked_by(card_id).len(), 0);assert_eq!(graph.children(card_id).len(), 0);assert_eq!(graph.parents(card_id).len(), 0);
Permanent deletion is irreversible. Use archiving for recoverable removal.
let feature_a = create_card("Feature A");let feature_b = create_card("Feature B");let integration = create_card("Integration Test");// Integration depends on both featuresgraph.add_blocks(feature_a, integration)?;graph.add_blocks(feature_b, integration)?;// Features are related but independentgraph.add_relates_to(feature_a, feature_b)?;