-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat: add dependency visualization to roadmap features #718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
ad7cea9
e711a8d
1a840c7
1399e0e
09b3dd9
3324304
d743638
188392d
33b941a
b8b3ca9
e575475
977daf4
6af9728
cf955ad
b7f3990
ae3c37e
60f748e
c88fd2a
5eee441
92074cb
c89f6c4
b5d4375
f74a687
a828f7c
df6f6aa
639e344
6d19468
5cdeb04
aa6c48e
27974c8
8d59cad
1eb8e9f
f36ff4f
3ff2384
5016b8f
8af25a8
7a35849
34a5401
85827de
6cf657e
f7cf2f2
9474e6c
03a1144
1cf47b2
c3fa5a7
b8b759a
4dc5025
fa78a50
812813b
72caed3
d8f2302
21cef5b
3bb2d53
4ce8ab7
abf0950
848ff65
03b0c8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,21 @@ | ||
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
| - **Roadmap:** Dependency visualization in all roadmap views | ||
| - **Roadmap:** Bidirectional dependency display (dependencies and reverse dependencies) | ||
| - **Roadmap:** Dependency validation with circular and missing dependency detection | ||
| - **Roadmap:** Clickable dependency chips with detail side panel | ||
| - **Roadmap:** Dependency status indicators (completed, in-progress, planned, missing) | ||
| - **Roadmap:** Reverse dependency calculation and display | ||
|
|
||
| ### Changed | ||
| - Enhanced roadmap data model with dependency metadata | ||
| - Improved roadmap refresh with dependency preservation (TODO in future phase) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Document the TODO or track it separately. The line mentions "TODO in future phase" for dependency preservation. Consider creating a tracking issue for this future work or clarifying the timeline. Would you like me to help create an issue to track this future enhancement? 🤖 Prompt for AI Agents |
||
|
|
||
| ### Fixed | ||
| - Duplicate circular dependency path reporting in validator | ||
| - Missing i18n namespace registration for roadmap translations | ||
|
|
||
| ## 2.7.2 - Stability & Performance Enhancements | ||
|
|
||
| ### ✨ New Features | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| """ | ||
| Dependency validators for roadmap features. | ||
| """ | ||
|
|
||
| from dataclasses import dataclass | ||
|
|
||
| # Import RoadmapFeature based on execution context | ||
| # When run as module: use relative import | ||
| # When run as script: use absolute import | ||
| if __package__: | ||
| from .models import RoadmapFeature | ||
| else: | ||
| from runners.roadmap.models import RoadmapFeature | ||
|
|
||
|
|
||
| @dataclass | ||
| class ValidationResult: | ||
| """Result of dependency validation.""" | ||
|
|
||
| has_missing: bool | ||
| has_circular: bool | ||
| missing_ids: list[str] | ||
| circular_paths: list[list[str]] | ||
| reverse_deps_map: dict[str, list[str]] | ||
|
|
||
|
|
||
| class DependencyValidator: | ||
| """Validates and enriches feature dependencies.""" | ||
|
|
||
| def validate_all(self, features: list[RoadmapFeature]) -> ValidationResult: | ||
| """ | ||
| Validates all dependencies in the roadmap. | ||
|
|
||
| Args: | ||
| features: List of features to validate | ||
|
|
||
| Returns: | ||
| ValidationResult with validation metadata | ||
| """ | ||
| # Find missing dependencies | ||
| missing_ids = self._find_missing_deps(features) | ||
|
|
||
| # Detect circular dependencies | ||
| circular_paths = self._detect_circular_deps(features) | ||
|
|
||
| # Calculate reverse dependencies | ||
| reverse_deps_map = self._calculate_reverse_deps(features) | ||
|
|
||
| return ValidationResult( | ||
| has_missing=len(missing_ids) > 0, | ||
| has_circular=len(circular_paths) > 0, | ||
| missing_ids=missing_ids, | ||
| circular_paths=circular_paths, | ||
| reverse_deps_map=reverse_deps_map, | ||
| ) | ||
|
|
||
| def _find_missing_deps(self, features: list[RoadmapFeature]) -> list[str]: | ||
| """Find dependencies that reference non-existent features.""" | ||
| valid_ids = {f.id for f in features} | ||
| missing = set() | ||
|
|
||
| for feature in features: | ||
| for dep_id in feature.dependencies: | ||
| if dep_id not in valid_ids: | ||
| missing.add(dep_id) | ||
|
|
||
| return sorted(missing) | ||
|
|
||
| def _detect_circular_deps(self, features: list[RoadmapFeature]) -> list[list[str]]: | ||
| """ | ||
| Detect circular dependencies using three-color DFS. | ||
|
|
||
| Uses WHITE (0), GRAY (1), BLACK (2) marking: | ||
| - WHITE: Not yet visited | ||
| - GRAY: Currently being processed (in current path) | ||
| - BLACK: Fully processed | ||
|
|
||
| Time complexity: O(V + E) where V = features, E = dependencies | ||
| """ | ||
| graph = {f.id: f.dependencies for f in features} | ||
| circular_paths = [] | ||
| seen_cycles = set() # Track normalized cycles for deduplication | ||
|
|
||
| # Three-color DFS: WHITE=0, GRAY=1, BLACK=2 | ||
| WHITE, GRAY, BLACK = 0, 1, 2 | ||
| color = {f.id: WHITE for f in features} | ||
|
|
||
| def normalize_cycle(cycle: list[str]) -> str: | ||
| """Rotate cycle to start from smallest ID for deduplication.""" | ||
| if not cycle or len(cycle) < 2: | ||
| return "" | ||
| # Remove last element (duplicate of first) | ||
| cycle_without_dup = cycle[:-1] | ||
| min_idx = cycle_without_dup.index(min(cycle_without_dup)) | ||
| # Rotate to start from minimal element | ||
| rotated = cycle_without_dup[min_idx:] + cycle_without_dup[:min_idx] | ||
| return ",".join(rotated) | ||
|
|
||
| def dfs(node: str, path: list[str]) -> None: | ||
| """DFS with backtracking - O(V + E) complexity.""" | ||
| color[node] = GRAY | ||
| path.append(node) | ||
|
|
||
| for neighbor in graph.get(node, []): | ||
| if neighbor not in graph: | ||
| continue # Skip non-existent nodes | ||
|
|
||
| if color[neighbor] == GRAY: | ||
| # Found a cycle - neighbor is in current path | ||
| cycle_start = path.index(neighbor) | ||
| cycle = path[cycle_start:] + [neighbor] | ||
| # Normalize and deduplicate | ||
| normalized = normalize_cycle(cycle) | ||
| if normalized not in seen_cycles: | ||
| seen_cycles.add(normalized) | ||
| circular_paths.append(cycle) | ||
| elif color[neighbor] == WHITE: | ||
| # Recurse into unvisited nodes | ||
| dfs(neighbor, path) | ||
|
|
||
| # Backtrack | ||
| path.pop() | ||
| color[node] = BLACK | ||
|
|
||
| # Run DFS from each unvisited node | ||
| for feature_id in graph: | ||
| if color[feature_id] == WHITE: | ||
| dfs(feature_id, []) | ||
|
|
||
| return circular_paths | ||
|
|
||
| def _calculate_reverse_deps( | ||
| self, features: list[RoadmapFeature] | ||
| ) -> dict[str, list[str]]: | ||
| """Calculate which features depend on each feature.""" | ||
| reverse_deps: dict[str, list[str]] = {} | ||
|
|
||
| # Initialize all features with empty list | ||
| for feature in features: | ||
| reverse_deps[feature.id] = [] | ||
|
|
||
| # Build reverse dependency map | ||
| for feature in features: | ||
| for dep_id in feature.dependencies: | ||
| if dep_id not in reverse_deps: | ||
| reverse_deps[dep_id] = [] | ||
| reverse_deps[dep_id].append(feature.id) | ||
|
|
||
| return reverse_deps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Minor: Add blank lines around section headings.
Per MD022, headings should be surrounded by blank lines for better readability and markdown best practices.
🔎 Proposed fixes
## [Unreleased] + ### Added - **Roadmap:** Dependency visualization in all roadmap views- **Roadmap:** Reverse dependency calculation and display + ### Changed - Enhanced roadmap data model with dependency metadata- Improved roadmap refresh with dependency preservation (TODO in future phase) + ### Fixed - Duplicate circular dependency path reporting in validatorAlso applies to: 11-11, 15-15
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents