Skip to content

Latest commit

Β 

History

History
715 lines (587 loc) Β· 27.2 KB

File metadata and controls

715 lines (587 loc) Β· 27.2 KB

Dependency Resolution Tasks

Module: lash-core (dependency resolver) Dependencies: tasks.core-data-model.md, tasks.sqlite-schema.md, tasks.markdown-parser.md Effort: 10-14 days Priority: CRITICAL

Overview

The dependency resolution system builds and analyzes the task dependency graph. It handles three types of dependencies:

  1. Implicit hierarchy - parent tasks depend on children
  2. Explicit cross-file references - @depends-on annotations
  3. Directory-level dependencies - directory structure relationships

It must detect cycles, compute completion status, identify blockers, and support efficient queries.

Core Requirements

From design-doc.md:

  • Model dependencies as a directed graph (section 5)
  • Detect circular dependencies (section 5)
  • Compute task completion status based on dependencies (section 5.4)
  • Support within-file and cross-file dependencies (sections 5.1, 5.3)
  • Handle waived tasks (section 5.1)
  • Identify blocked tasks (section 5.4)

Task 1: Graph Data Structure

Priority: CRITICAL Effort: 2-3 days Depends on: tasks.core-data-model.md#1

Description

Define the in-memory graph representation for the task dependency network.

Subtasks

  • Define DependencyGraph struct
    • Node storage (task IDs -> Task references)
    • Edge storage (adjacency list: task_id -> Vec<task_id>)
    • Reverse edge storage (for efficient reverse lookups)
    • Edge metadata (dependency type: hierarchy, explicit, directory)
  • Implement graph construction from DB
    • Load all tasks and dependencies from SQLite
    • Build adjacency lists
    • Index tasks by full_id for fast lookup
  • Implement graph query methods
    • get_dependencies(task_id) - direct dependencies
    • get_dependents(task_id) - reverse dependencies
    • get_descendants(task_id) - transitive closure (DFS/BFS)
    • get_ancestors(task_id) - reverse transitive closure
  • Add edge type tracking
    • Distinguish hierarchy vs explicit vs directory edges
    • Store source location for explicit edges (for error reporting)

Success Criteria

  • Graph correctly represents all dependency relationships
  • Efficient lookups: O(1) for direct dependencies, O(E+V) for transitive
  • Memory-efficient for large graphs (1000+ tasks)
  • Clear API for downstream consumers

Tests

  • Unit: Build graph from fixture data
  • Unit: Test query methods on various graph structures
  • Unit: Test edge type tracking
  • Performance: Measure memory usage for large graphs

Task 2: Cycle Detection

Priority: CRITICAL Effort: 2-3 days Depends on: Task 1

Description

Detect circular dependencies in the task graph using standard graph algorithms.

Subtasks

  • Implement CycleDetector struct
    • Use DFS with color marking (white/gray/black)
    • Track path during traversal for cycle reporting
  • Implement detect_cycles() function
    • Run DFS from all unvisited nodes
    • Detect back edges (gray -> gray)
    • Collect all cycles (not just first)
    • Return cycle paths with task IDs
  • Add cycle reporting
    • Format cycle as: Task A -> Task B -> Task C -> Task A
    • Include file paths and line numbers
    • Distinguish cycle types (within-file vs cross-file)
  • Implement cycle resolution suggestions
    • Identify weakest link (e.g., directory dep vs explicit)
    • Suggest breaking explicit dependencies
    • Suggest restructuring hierarchy

Success Criteria

  • Detects all cycles in arbitrary graphs
  • Correctly handles graphs with multiple disjoint cycles
  • Clear, actionable error messages for each cycle
  • No false positives or false negatives

Tests

  • Unit: Acyclic graph (no cycles)
  • Unit: Simple cycle (A -> B -> A)
  • Unit: Complex cycle (A -> B -> C -> D -> B)
  • Unit: Multiple disjoint cycles
  • Unit: Self-loop (A -> A)
  • Integration: Test with fixture files containing cycles

Task 3: Dependency Resolution Engine

Priority: CRITICAL Effort: 3-4 days Depends on: Task 1, tasks.markdown-parser.md#2

Description

Parse dependency references from tasks and build the complete dependency graph.

Subtasks

  • Implement DependencyResolver struct
    • Hold references to parsed tasks and files
    • Track resolution errors (broken links)
  • Implement implicit hierarchy resolution
    • For each task, add edges to all children
    • Edges are (parent_id, child_id, type=hierarchy)
    • Note: Implemented in indexing layer (DependencyUpdater.insert_hierarchy_dependencies())
  • Implement explicit @depends-on resolution
    • Parse @depends-on annotations
    • Support formats:
      • Relative path: ../core/cli.md#task:parse-args
      • Absolute path: core/cli.md#task:parse-args
      • ID-only: #task:parse-args (within-file)
      • File-level: ../core/cli.md (depends on all top-level tasks in file)
      • Bare file ID: file-id (file-level dependency via ID)
    • Resolve paths relative to source file
    • Look up target task in DB/graph
    • Create edge (source_id, target_id, type=explicit)
    • Handle missing targets (broken links)
  • Implement directory-level dependencies
    • Parse directory references (format: path/to/dir/)
    • Find all files in the target directory (immediate children only)
    • Create dependencies to top-level tasks in each file
    • Report error if directory contains no task files
  • Handle broken dependencies
    • Collect all broken references
    • Return structured errors with source location
    • Mark affected tasks as potentially blocked - implemented in status computation

Success Criteria

  • Correctly resolves all dependency types (explicit ID, explicit path, within-file, file-level, directory)
  • Handles all supported reference formats
  • Detects and reports broken links with precise locations
  • Produces complete, accurate dependency graph

Tests

  • Unit: Resolve implicit hierarchy - tests in indexing layer (test_hierarchy_dependencies_created)
  • Unit: Resolve explicit cross-file dependencies
  • Unit: Resolve various path formats
  • Unit: Handle broken links gracefully
  • Unit: File-level dependencies (path and bare ID formats)
  • Unit: Directory dependencies (basic, top-level only, not recursive, not found)
  • [-] Integration: Build graph from fixture project - deferred
  • [-] Integration: Verify graph structure matches expectations - deferred

Implementation Notes

Completed:

  • Created crates/lash-core/src/dependency/resolver.rs with DependencyResolver
  • Supports path-based and ID-based dependency references
  • Handles relative path resolution with normalize_path() helper
  • Collects all resolution errors without failing fast
  • Provides detailed error messages with source locations
  • All unit tests passing (15 tests)
  • All doctests passing (3 tests)

File-Level Dependencies (December 2024):

  • Implemented file-level dependencies: ../path/file.md (without #task fragment)
  • Implemented bare file ID dependencies: file-id (without #task-id)
  • Both formats create dependencies to all top-level tasks (depth == 0) in the target file
  • Nested tasks are excluded to avoid bloating the dependency graph

Directory-Level Dependencies (December 2024):

  • Implemented directory reference resolution: path/to/dir/ (must end with /)
  • Creates dependencies to top-level tasks in all files directly in the directory
  • Does NOT recurse into subdirectories (immediate children only)
  • Reports DirectoryNotFound error if directory contains no task files
  • Added ResolutionErrorKind::DirectoryNotFound variant for error handling
  • Changed resolve_reference() to return Vec<ResolvedDependency> to support multiple targets

Test Coverage (7 new tests):

  • test_file_level_dependency_path_reference - verifies top-level task filtering
  • test_file_level_dependency_bare_file_id - bare file ID format
  • test_directory_dependency_basic - multiple files in directory
  • test_directory_dependency_top_level_only - excludes nested tasks
  • test_directory_dependency_not_recursive - excludes subdirectories
  • test_directory_dependency_not_found - error handling
  • test_directory_dependency_kind - verifies DependencyKind::Directory

Changes to lash-types:

  • Fixed parse_dependency_ref() to correctly detect path references with # fragments
  • Fixed DependencyRef::validate() to check only the path part (before #)
  • Added TaskTree::get_task_mut() for modifying tasks in tests

Task 4: Completion Status Computation

Priority: HIGH Effort: 2-3 days Depends on: Task 1, Task 3

Description

Compute the effective completion status of each task based on its own status and its dependencies.

Subtasks

  • Implement StatusComputer struct
    • Take graph and task statuses as input
    • Compute effective status for each task
  • Implement completion rules (from design doc section 5.4)
    • Task is complete if:
      • Own status is done, AND
      • All children are done or waived, AND
      • All explicit dependencies are complete or waived
    • Task is blocked if:
      • Any dependency is open or blocked (not waived), OR
      • Depends on broken link
  • Implement compute_status() function
    • Topological traversal (or recursive with memoization)
    • Cache computed statuses to avoid recomputation
    • Handle waived dependencies (ignore in completion check)
  • Add file-level completion status
    • File complete if all top-level tasks complete
    • [-] Consider directory-level dependencies (deferred)
  • Detect inconsistencies
    • Parent marked done but children open (lint warning)
    • Task marked done but dependencies open

Success Criteria

  • Correctly computes status for all tasks in graph
  • Respects waived tasks (treats as complete)
  • Identifies blocked tasks accurately
  • Efficient: O(V+E) traversal, with memoization

Tests

  • Unit: Simple chain (A -> B -> C), various states
  • Unit: Waived dependencies ignored
  • Unit: Blocked propagation (A blocks B, B blocks C)
  • Unit: Parent/child status consistency
  • [-] Integration: Compute status for entire fixture project (deferred)

Implementation Notes

Completed:

  • Created crates/lash-core/src/dependency/status_computer.rs with complete implementation
  • Implemented ComputedStatus enum with Complete, Incomplete, Blocked, and Inconsistent variants
  • Implemented BlockerReason enum to provide detailed information about why tasks are blocked
  • Implemented InconsistencyKind enum to identify different types of status inconsistencies
  • Used recursive DFS with memoization for efficient O(V+E) status computation
  • Handles cycle detection during status computation
  • Distinguishes between hierarchy and explicit dependencies for inconsistency detection
  • File-level completion status computed based on top-level tasks (depth 0)
  • All unit tests passing (14 tests)
  • All doctests passing (4 tests)
  • Exported from crates/lash-core/src/dependency/mod.rs

Algorithm:

  • Recursive status computation with memoization cache
  • Uses visiting set for cycle detection
  • Waived tasks always treated as complete
  • Blocked status propagates through dependency chains
  • Inconsistencies detected when done tasks have incomplete dependencies
  • Separates parent/child inconsistencies from explicit dependency inconsistencies

Test Coverage:

  • Single task states (done, open, waived)
  • Simple dependency chains
  • Waived dependency handling
  • Blocked dependency propagation
  • Multiple blockers
  • Parent-child inconsistencies
  • Done tasks with incomplete explicit dependencies
  • File-level status computation
  • Cycle detection

Deferred:

  • Directory-level dependencies (not yet implemented in graph)
  • Integration tests with full fixture projects (to be added later)

Task 5: Blocker Identification

Priority: HIGH Effort: 2 days Depends on: Task 4

Description

Given a task, identify which dependencies are blocking its completion.

Subtasks

  • Implement BlockerAnalyzer struct
    • Query graph and status for dependencies
    • Identify incomplete dependencies
  • Implement find_blockers(task_id) function
    • Get all dependencies (direct + transitive)
    • Filter to incomplete/blocked tasks
    • Sort by "distance" (direct blockers first)
    • Return list of blocker tasks with reasons
  • Add blocker chain analysis
    • For each blocker, recursively find its blockers
    • Build blocker tree/graph
    • Identify "root blockers" (no further dependencies)
  • Implement blocker reporting
    • Format: "Task X is blocked by: Task Y (in file Z)"
    • Show full blocker chain for deep dependencies
    • Suggest actions (complete blockers, waive, remove dependency)

Success Criteria

  • Accurately identifies all blockers for a given task
  • Handles transitive blockers (A blocked by B blocked by C)
  • Clear, actionable blocker reports
  • Efficient: O(E) for direct blockers, O(V+E) for transitive

Tests

  • Unit: Task with direct blocker
  • Unit: Task with transitive blocker chain
  • Unit: Task with multiple independent blockers
  • Unit: Task with no blockers (ready to start)
  • Integration: Generate blocker report for fixture tasks

Implementation Notes

Completed:

  • Created crates/lash-core/src/dependency/blocker_analyzer.rs with complete implementation
  • Implemented BlockerAnalyzer with comprehensive blocker identification
  • Implemented BlockerInfo struct with depth tracking and blocker metadata
  • Implemented BlockerChain for showing transitive blocker relationships
  • Implemented BlockerReport with human-readable formatting
  • Implemented BlockerSuggestion enum for actionable resolution suggestions
  • Uses BFS to find all blockers with depth tracking (0=direct, 1+=transitive)
  • Root blocker identification (tasks with no incomplete dependencies)
  • Deduplication to avoid repeated blockers via multiple paths
  • All unit tests passing (7 tests)
  • All doctests passing (7 tests)
  • Exported from crates/lash-core/src/dependency/mod.rs

Algorithm:

  • BFS traversal starting from direct dependencies
  • Depth tracking to distinguish direct vs transitive blockers
  • Only follows paths through blocked or incomplete tasks
  • Sorts results by depth (direct blockers first)
  • Root blockers identified as incomplete tasks with no blockers themselves

Data Structures:

  • BlockerInfo: Contains task_id, title, file_id, depth, dependency_kind, and blocker_status
  • BlockerChain: Shows recursive blocker relationships from direct to root
  • BlockerReport: Formatted output with blockers, chains, roots, and suggestions
  • BlockerSuggestion: Actionable recommendations (complete, waive, remove dependency)

Report Format:

  • Summary: Total blockers, direct vs transitive counts
  • Root blockers section (most important - address first)
  • Blocker chains showing dependency paths (A β†’ B β†’ C)
  • All blockers listed with depth and status
  • Suggested actions prioritizing root blockers

Test Coverage:

  • Direct blocker identification
  • Transitive blocker chains
  • Multiple independent blockers
  • No blockers (ready to start)
  • Completed dependencies not treated as blockers
  • Blocker chain construction
  • Report generation and formatting

Integration:

  • Uses DependencyGraph for traversal
  • Uses StatusComputer::compute_all() for task statuses
  • Builds on existing ComputedStatus from status_computer
  • Provides detailed analysis beyond basic status computation

Task 6: Graph Export

Priority: MEDIUM Effort: 1-2 days Depends on: Task 1

Description

Export dependency graph in various formats for visualization and analysis.

Subtasks

  • Implement GraphExporter struct
    • Support multiple output formats
  • Implement DOT format export
    • Generate Graphviz-compatible DOT file
    • Nodes: task IDs or titles
    • Edges: dependency relationships
    • Color-code by status (green=done, yellow=open, coral=blocked, gray=waived)
    • Cluster by file or directory
  • Implement JSON export
    • Nodes array: task metadata
    • Edges array: source/target/type
    • Include status (labels not yet in NodeData)
  • Add filtering options
    • Export subgraph (specific file or label)
    • Hide completed tasks
    • Show only direct dependencies (max_depth option)
  • Implement text-based graph visualization
    • ASCII tree format for terminal display
    • Indent by depth
    • Show dependency arrows with status indicators

Success Criteria

  • DOT output renders correctly in Graphviz (valid syntax verified)
  • JSON format is parsable and complete
  • Filtering options work as expected
  • Text format is readable in terminal

Tests

  • Unit: Export empty graph (DOT and JSON)
  • Unit: Export simple graph to DOT
  • Unit: Export simple graph to JSON
  • Unit: Export ASCII tree (simple and nested)
  • Unit: Filter by file
  • Unit: Filter by completion status
  • Unit: Filter by max depth
  • Unit: Multiple file clustering
  • Unit: Cycle detection in ASCII tree
  • Unit: DOT special character escaping
  • [-] Integration: Export fixture project graph, verify correctness (deferred)
  • [-] Manual: Render DOT file with Graphviz, inspect visually (deferred)

Implementation Notes

Completed:

  • Created crates/lash-core/src/dependency/graph_exporter.rs with complete implementation
  • Implemented GraphExporter struct with three export formats
  • Implemented FilterOptions for flexible subgraph export
  • DOT format with Graphviz syntax:
    • Color-coded nodes (lightgreen=done, lightyellow=open, lightcoral=blocked, lightgray=waived)
    • File-based clustering with subgraphs
    • Labeled edges with dependency kind
    • Proper escaping of special characters
  • JSON format with serde serialization:
    • Separate nodes and edges arrays
    • Full task metadata (id, title, status, file_id, depth)
    • Edge metadata (from, to, kind, source_location)
  • ASCII tree format for terminal display:
    • Recursive tree rendering with proper indentation
    • Status indicators: [ ] open, [βœ“] done, [-] waived, [!] blocked
    • Cycle detection to prevent infinite recursion
    • Box-drawing characters (└─, β”œβ”€, β”‚) for visual structure
  • Filter options:
    • Filter by file IDs
    • Hide completed tasks (done/waived)
    • Max depth for limiting transitive dependencies
    • Label filtering placeholder (labels not yet in NodeData)
  • All unit tests passing (12 tests)
  • All doctests passing (7 tests)
  • Exported from crates/lash-core/src/dependency/mod.rs

Data Structures:

  • GraphExporter<'a>: Borrows graph reference for export
  • FilterOptions: Configure which nodes/edges to include
  • JsonGraph, JsonNode, JsonEdge: Serde-compatible JSON representation

Export Formats:

  • DOT: Graphviz-compatible directed graph with clustering
  • JSON: Structured data for programmatic consumption
  • ASCII tree: Terminal-friendly visualization starting from a root node

Test Coverage:

  • Empty graph export (both formats)
  • Simple graphs with nodes and edges
  • ASCII tree rendering (simple and nested)
  • Filter by file
  • Filter by completion status
  • Filter by depth
  • Multiple file clustering
  • Cycle detection
  • Special character escaping

Integration:

  • Uses DependencyGraph for graph queries
  • Respects TaskStatus enum including Blocked state
  • Compatible with existing dependency module APIs

Task 7: Incremental Graph Updates

Priority: MEDIUM Effort: 2-3 days Depends on: Task 3, tasks.indexing.md#5

Description

Support efficient graph updates when tasks or dependencies change, without full rebuild.

Subtasks

Phase 1: Core Graph Mutation Operations (COMPLETE)

  • Define GraphError enum for error handling
  • Define GraphResult<T> type alias
  • Implement remove_node with force option
    • Error if node has dependents (unless force=true)
    • Remove all associated edges (incoming and outgoing)
    • Maintain bidirectional edge consistency
  • Implement update_node to replace node metadata
  • Implement update_node_status for status-only updates (optimized)
  • Implement remove_edge to remove dependency relationships
    • Update forward adjacency list
    • Update reverse adjacency list
    • Remove edge metadata
  • Implement update_edge to replace edge metadata
  • Add comprehensive unit tests (13 tests)
  • Add doctests for all public mutation methods
  • Export GraphError and GraphResult from mod.rs

Phase 2: Batch Update Operations (COMPLETE)

  • Implement add_nodes for bulk node insertion
    • Pre-allocate space to minimize reallocations
  • Implement remove_nodes for bulk node removal
    • Fail-fast on first error (or force remove all)
  • Implement add_edges for bulk edge insertion
    • Pre-allocate space for edge metadata
  • Implement remove_edges for bulk edge removal
    • Fail-fast on first error
  • Add comprehensive unit tests (6 tests)
  • Add doctests for all batch operations

Phase 3: Incremental Dependency Re-resolution (COMPLETE)

  • Create GraphChanges struct to track modifications
    • Track added/removed/modified nodes
    • Track status-only changes separately
    • Track added/removed/modified edges
  • Implement change classification methods
    • has_structural_changes() - detect graph structure changes
    • is_status_only() - detect pure status updates
    • is_empty() - check if any changes
  • Implement compute_affected_nodes to determine recomputation scope
    • Include all modified nodes
    • Propagate to all ancestors (transitive dependents)
    • Handle edge changes correctly
  • Implement utility methods
    • merge() - combine multiple change sets
    • clear() - reset change tracker
  • Add comprehensive unit tests (11 tests)
  • Add doctests with realistic examples

Phase 4: Optimization for Common Cases (IMPLEMENTED)

  • Fast path for status-only changes (via update_node_status)
    • O(1) status update without copying node data
  • Change detection via GraphChanges
    • has_structural_changes() determines if cycle detection needed
    • is_status_only() enables optimized status recomputation
  • [-] Benchmarks comparing incremental vs full rebuild (deferred)
    • Note: Benchmarks deferred to future performance optimization phase
  • All common operations optimized for minimal allocations

Phase 5: Integration and Documentation (COMPLETE)

  • Comprehensive doctests for all public APIs
    • All mutation methods (5 doctests)
    • All batch operations (4 doctests)
    • GraphChanges with realistic usage (2 doctests)
  • Unit test coverage
    • Phase 1: 13 tests (mutation operations)
    • Phase 2: 6 tests (batch operations)
    • Phase 3: 11 tests (change tracking)
    • Total: 30 new tests, all passing
  • Clear error messages via GraphError enum
  • Performance characteristics documented in doc comments
  • [-] Integration tests with full workflow (deferred to future)
    • Note: Graph mutation layer complete; integration with resolver deferred

Success Criteria

  • Core mutation operations maintain graph invariants
  • Comprehensive test coverage (30 unit tests, 11 doctests)
  • Clear error handling with helpful messages (GraphError enum)
  • Graph remains consistent after all operations
  • Change tracking enables incremental updates
  • Optimized for common cases (status-only updates)

Tests

  • Unit: Remove node (simple, with dependents, force removal) - 4 tests
  • Unit: Update node metadata and status - 4 tests
  • Unit: Remove/update edges - 5 tests
  • Unit: Graph invariants maintained after mutations - all tests verify
  • Unit: Batch operations - 6 tests
  • Unit: Change tracking (GraphChanges) - 11 tests
  • [-] Integration: Incremental update after file modification (deferred)
  • [-] Benchmarks: Performance comparison (deferred)

Implementation Notes

All Phases Complete (Task 7):

Phase 1 - Core Mutations:

  • Added GraphError enum with three variants:
    • NodeNotFound: Node doesn't exist
    • EdgeNotFound: Edge doesn't exist
    • NodeHasDependents: Cannot remove node with dependents
  • Implemented 5 mutation methods on DependencyGraph:
    • remove_node(task_id, force): Remove node and edges
    • update_node(task_id, node_data): Replace node metadata
    • update_node_status(task_id, status): Optimized status update (O(1))
    • remove_edge(from_id, to_id): Remove dependency relationship
    • update_edge(from_id, to_id, edge_data): Replace edge metadata
  • Added internal helper remove_edge_internal for efficient bulk removal
  • All mutation methods maintain bidirectional edge consistency
  • 13 unit tests + 5 doctests

Phase 2 - Batch Operations:

  • Implemented 4 batch methods:
    • add_nodes(nodes): Bulk node insertion with pre-allocation
    • remove_nodes(task_ids, force): Bulk node removal
    • add_edges(edges): Bulk edge insertion with pre-allocation
    • remove_edges(edges): Bulk edge removal
  • All batch operations optimize for minimal reallocations
  • Fail-fast error handling (returns first error encountered)
  • 6 unit tests + 4 doctests

Phase 3 - Change Tracking:

  • Implemented GraphChanges struct with comprehensive change tracking:
    • Tracks added/removed/modified nodes
    • Tracks status-only changes separately
    • Tracks added/removed/modified edges
  • Change classification methods:
    • has_structural_changes(): Detect graph structure changes
    • is_status_only(): Detect pure status updates
    • is_empty(): Check if any changes occurred
  • Implemented compute_affected_nodes(graph):
    • Computes transitive closure of affected nodes
    • Propagates changes to all ancestors
    • Enables incremental status recomputation
  • Utility methods: merge(), clear()
  • 11 unit tests + 2 doctests

Phase 4 - Optimizations:

  • Status-only updates use O(1) in-place mutation
  • Batch operations pre-allocate to minimize reallocations
  • Change detection enables smart recomputation
  • Structural changes β†’ full cycle detection needed
  • Status-only changes β†’ incremental status update sufficient

Test Summary:

  • Total: 30 new unit tests, 11 new doctests
  • All tests passing (66 unit tests, 34 doctests overall)
  • Full coverage of mutation operations, batch operations, and change tracking
  • All graph invariants maintained across all operations

Exports:

  • Added to dependency/mod.rs:
    • GraphError, GraphResult
    • GraphChanges
  • All public APIs have executable doctests

Non-Goals (for v1)

  • Advanced graph algorithms (shortest path, etc.)
  • Graph persistence to disk (rebuild from DB each time)
  • Multi-graph support (separate graphs for different views)
  • Real-time graph updates (manual recomputation)

Open Questions

  • Cycle handling: Fail-fast vs collect all cycles?
  • Waived propagation: If A waives B, does B's status matter?
  • Performance: Build graph on-demand vs cache in memory?
  • Directory dependencies: Explicit annotation vs inferred from structure?

References

  • Design doc section 5 (Dependency model)
  • Design doc section 5.4 (Completion semantics)
  • Design doc section 7.3.4 (Graph commands)
  • /docs/dependency-graph-architecture.md - Graph architecture design