Skip to content

feat: Add task dependencies and spawn sub-task functionality#243

Merged
webdevcody merged 6 commits intoAutoMaker-Org:mainfrom
JBotwina:JBotwina/task-deps-spawn
Dec 24, 2025
Merged

feat: Add task dependencies and spawn sub-task functionality#243
webdevcody merged 6 commits intoAutoMaker-Org:mainfrom
JBotwina:JBotwina/task-deps-spawn

Conversation

@JBotwina
Copy link
Contributor

@JBotwina JBotwina commented Dec 23, 2025

Summary

Adds task dependency management and sub-task spawning to the graph and kanban views.

Features

  • Edge dragging to create dependencies - Drag from one node to another in graph view to create a dependency relationship
  • Circular dependency prevention - Validates that new dependencies won't create cycles
  • Duplicate dependency detection - Prevents adding the same dependency twice
  • Delete dependencies - Hover over an edge to reveal a delete button, or select the edge and click delete
  • Delete tasks from graph view - Added delete option to the node dropdown menu
  • Spawn sub-task action - Available in both graph view node dropdown and kanban card menus
  • Ancestor context selection - When spawning, optionally include parent/ancestor context (description, spec, summary) in the new task's prompt
  • Automatic dependency linking - Spawned tasks automatically depend on their parent task

Test plan

  • Drag between nodes in graph view to create dependencies
  • Verify circular dependencies are blocked with error toast
  • Verify duplicate dependencies show info toast
  • Hover over an edge and click the delete button to remove dependency
  • Use node dropdown menu to delete a task
  • Use "Spawn Sub-Task" from graph view node dropdown
  • Use "Spawn Sub-Task" from kanban cards (all statuses)
  • Verify ancestor context selection UI with collapsible previews
  • Confirm spawned task includes selected ancestor context in description
  • Verify parent task is automatically set as a dependency

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Spawn Sub-Task: Create child tasks directly from existing tasks in board and graph views with context-aware parent assignment
    • Dependency Management: Create and delete task dependencies between items with automatic validation to prevent circular relationships
    • Ancestor Context Display: View parent and ancestor task details when creating subtasks to maintain project lineage
  • Improvements

    • Graph view now preserves node positions and layout when updates occur

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Introduces spawn sub-task functionality across UI components with dependency management capabilities. Adds collapsible component dependency, enhances dialogs/boards/graphs with spawn callbacks, implements ancestor context display, and adds utilities for circular dependency detection and dependency validation.

Changes

Cohort / File(s) Summary
UI Component Library
apps/ui/package.json, apps/ui/src/components/ui/collapsible.tsx
Added @radix-ui/react-collapsible dependency and created wrapper component exporting Collapsible, CollapsibleTrigger, and CollapsibleContent primitives.
Board View Integration
apps/ui/src/components/views/board-view.tsx, apps/ui/src/components/views/board-view/kanban-board.tsx
Threaded onSpawnTask callback through BoardView to KanbanBoard and GraphView; wired spawn mode state and passed parentFeature and allFeatures to AddFeatureDialog.
Kanban Card Spawn Controls
apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx, apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx
Added onSpawnTask prop threading; exposed spawn sub-task actions via buttons/menu items with GitFork icon across multiple task states (backlog, in_progress, waiting_approval, verified).
Add Feature Dialog Enhancement
apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx
Added spawn mode logic with parentFeature and allFeatures props; integrated AncestorContextSection; prepended ancestor context to feature description; updated confirm button label and added dependencies field to payload.
Feature Creation Hook
apps/ui/src/components/views/board-view/hooks/use-board-actions.ts
Extended handleAddFeature to accept and propagate optional dependencies field into created feature data.
Ancestor Context Display
apps/ui/src/components/views/board-view/shared/ancestor-context-section.tsx, apps/ui/src/components/views/board-view/shared/index.ts
New component for displaying and selecting parent/ancestor hierarchy with checkboxes, collapsible details, and bulk controls; exported from shared module.
Graph View Dependency & Node Management
apps/ui/src/components/views/graph-view/graph-view.tsx, apps/ui/src/components/views/graph-view/graph-canvas.tsx, apps/ui/src/components/views/graph-view/components/task-node.tsx, apps/ui/src/components/views/graph-view/components/dependency-edge.tsx
Added target/source handles to nodes; implemented edge creation with circular dependency and duplicate checks; added spawn/delete callbacks to nodes; added hover-triggered delete button to edges with validation.
Graph Hooks & Utilities
apps/ui/src/components/views/graph-view/hooks/use-graph-nodes.ts, apps/ui/src/components/views/graph-view/hooks/use-graph-layout.ts
Extended TaskNodeData and DependencyEdge with spawn/delete callbacks; added node position caching to preserve layout on structural changes.
Dependency Resolver Library
libs/dependency-resolver/src/resolver.ts, libs/dependency-resolver/src/index.ts, libs/dependency-resolver/tests/resolver.test.ts
Added wouldCreateCircularDependency, dependencyExists, getAncestors, and formatAncestorContextForPrompt utilities with comprehensive test coverage for cycle detection and ancestor retrieval.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant KanbanBoard as Kanban View
    participant Dialog as Add Feature<br/>Dialog
    participant BoardView as Board View<br/>(State)
    participant AddFeatureAPI as Feature API

    User->>KanbanBoard: Click "Spawn Sub-Task"
    KanbanBoard->>BoardView: onSpawnTask(parentFeature)
    activate BoardView
    BoardView->>BoardView: setSpawnParentFeature(feature)
    BoardView->>Dialog: open with parentFeature
    deactivate BoardView

    User->>Dialog: Select ancestors<br/>Enter description
    User->>Dialog: Click "Spawn Task"
    activate Dialog
    Dialog->>Dialog: formatAncestorContextForPrompt()
    Dialog->>Dialog: Prepend context to description
    Dialog->>AddFeatureAPI: onAdd({...featureData, dependencies: [parentId]})
    deactivate Dialog

    AddFeatureAPI->>BoardView: Feature created with dependency
    BoardView->>KanbanBoard: Refresh board
    User-->>KanbanBoard: New sub-task appears
Loading
sequenceDiagram
    actor User
    participant GraphView as Graph View
    participant GraphCanvas as Graph Canvas<br/>(React Flow)
    participant ValidationFn as Validation Logic
    participant UpdateAPI as Update Feature<br/>API
    participant Toast as Toast Notifier

    User->>GraphView: Drag edge from Node A → Node B
    GraphView->>GraphCanvas: Edge connection event
    activate GraphCanvas
    GraphCanvas->>ValidationFn: isValidConnection(A, B)
    ValidationFn-->>GraphCanvas: true/false
    deactivate GraphCanvas

    alt Valid Connection
        GraphCanvas->>GraphView: onCreateDependency(A, B)
        activate GraphView
        GraphView->>ValidationFn: wouldCreateCircularDependency()
        ValidationFn-->>GraphView: false (safe)
        GraphView->>ValidationFn: dependencyExists(A, B)
        ValidationFn-->>GraphView: false (new)
        GraphView->>UpdateAPI: onUpdateFeature(B, {dependencies: [A]})
        UpdateAPI-->>GraphView: Feature B updated
        GraphView->>Toast: "Dependency created"
        deactivate GraphView
    else Invalid (Self-link/Cycle/Duplicate)
        GraphView->>Toast: "Cannot create dependency"
    end

    User->>GraphView: Hover over edge → Click delete
    GraphView->>UpdateAPI: Remove dependency from target
    Toast->>User: "Dependency removed"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #211: Modifies KanbanBoard component props and callback signatures for feature spawning integration
  • PR #226: Updates graph-view hooks (use-graph-nodes.ts) with per-node action callbacks and edge metadata wiring
  • PR #222: Extends graph-view implementation (graph-canvas, task-node, dependency-edge) with dependency and node management features

Poem

🐰 Hopping through nodes with spawn so keen,
Dependencies circle, but never unseen!
Ancestors gathered, ancestors shown,
Sub-tasks now sprout from parent tasks grown. 🌱
With edges and handles, the forest takes shape,
No cycles allowed—we've made our escape!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.17% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Add task dependencies and spawn sub-task functionality' directly and clearly summarizes the main changes in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @JBotwina, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances task management capabilities by introducing visual dependency creation in the graph view and a streamlined process for spawning sub-tasks. These features aim to improve task organization, facilitate the breakdown of complex work, and ensure that new tasks are created with relevant historical context, ultimately leading to a more efficient and intuitive workflow.

Highlights

  • Task Dependencies via Edge Dragging: Users can now create dependency relationships between tasks in the graph view by dragging edges. This functionality includes robust validation to prevent circular dependencies, self-dependencies, and duplicate edges, ensuring a consistent and logical task flow.
  • Spawn Sub-Task Functionality: A new 'Spawn Sub-Task' action has been introduced, accessible from both the graph view's TaskNode dropdowns and the kanban board's card menus. When creating a sub-task, users can select ancestor tasks to automatically include their context (description, specification, agent output) in the new task's prompt, and the parent task is automatically set as a dependency.
  • Ancestor Context Selection UI: The 'Add Feature' dialog now dynamically adapts when spawning a sub-task, presenting a dedicated section for selecting ancestor contexts. This section features collapsible previews of ancestor details and allows users to choose which information to include in the sub-task's prompt.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@JBotwina JBotwina marked this pull request as draft December 23, 2025 16:03
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new 'Spawn Sub-Task' feature, enabling users to create new tasks that are automatically linked as dependencies to an existing parent task. Key changes include adding a new Collapsible UI component using Radix UI, integrating 'Spawn Sub-Task' actions into Kanban cards and graph nodes, and updating the AddFeatureDialog to support a 'spawn mode'. In spawn mode, the dialog now displays an 'Ancestor Context' section, allowing users to select which parent and ancestor information should be included in the new sub-task's description. The system also now automatically adds the parent as a dependency and includes new utility functions for traversing the dependency graph (getAncestors), formatting ancestor context for prompts, and validating dependencies to prevent self-dependencies, duplicate dependencies, and circular dependencies. Review comments highlight the need for more immediate visual feedback for dependency validation (e.g., preventing edge drawing or showing temporary red outlines for circular dependencies), suggest improving the onOpenChange logic for AddFeatureDialog to prevent stale data, and recommend extracting complex description logic into a helper function for better readability. Additionally, there are suggestions to use a more robust truncation utility for feature titles/descriptions and to refine the cycle detection logic in wouldCreateCircularDependency and getAncestors by correctly managing visited and recursionStack states to ensure accurate cycle identification.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/ui/src/components/ui/collapsible.tsx (1)

1-10: Unused React import.

The React import on line 1 is not used in this file since there's no JSX or React API usage—only re-exports of Radix primitives.

🔎 Proposed fix
-import * as React from 'react';
 import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';

 const Collapsible = CollapsiblePrimitive.Root;

 const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;

 const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;

 export { Collapsible, CollapsibleTrigger, CollapsibleContent };
apps/ui/src/components/views/board-view/shared/ancestor-context-section.tsx (1)

108-108: Consider accessibility improvement for the scrollable region.

The ancestor list is scrollable but lacks an ARIA label. Consider adding aria-label="Ancestor context list" or role="listbox" for better screen reader support.

🔎 Proposed fix
-      <div className="space-y-1 max-h-[200px] overflow-y-auto border rounded-lg p-2 bg-muted/20">
+      <div className="space-y-1 max-h-[200px] overflow-y-auto border rounded-lg p-2 bg-muted/20" aria-label="Ancestor context list">
apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx (1)

52-56: Consider relocating shared utilities.

The ancestor context utilities are imported from graph-view/utils into a board-view component. Since these utilities are now shared across views, consider moving them to a common location (e.g., shared/utils or lib/) to clarify the dependency direction.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e95912f and f780f53.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • apps/ui/package.json
  • apps/ui/src/components/ui/collapsible.tsx
  • apps/ui/src/components/views/board-view.tsx
  • apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx
  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
  • apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx
  • apps/ui/src/components/views/board-view/hooks/use-board-actions.ts
  • apps/ui/src/components/views/board-view/kanban-board.tsx
  • apps/ui/src/components/views/board-view/shared/ancestor-context-section.tsx
  • apps/ui/src/components/views/board-view/shared/index.ts
  • apps/ui/src/components/views/graph-view/components/task-node.tsx
  • apps/ui/src/components/views/graph-view/graph-canvas.tsx
  • apps/ui/src/components/views/graph-view/graph-view.tsx
  • apps/ui/src/components/views/graph-view/hooks/use-graph-nodes.ts
  • apps/ui/src/components/views/graph-view/utils/ancestor-context.ts
  • apps/ui/src/components/views/graph-view/utils/dependency-validation.ts
  • apps/ui/src/components/views/graph-view/utils/index.ts
🧰 Additional context used
🧬 Code graph analysis (4)
apps/ui/src/components/views/graph-view/graph-view.tsx (2)
apps/ui/src/store/app-store.ts (1)
  • Feature (256-272)
apps/ui/src/components/views/graph-view/utils/dependency-validation.ts (2)
  • dependencyExists (47-51)
  • wouldCreateCircularDependency (12-37)
apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx (1)
apps/ui/src/components/ui/button.tsx (1)
  • Button (108-108)
apps/ui/src/components/views/board-view.tsx (2)
apps/ui/src/store/app-store.ts (1)
  • Feature (256-272)
libs/types/src/feature.ts (1)
  • Feature (24-58)
apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx (4)
apps/ui/src/store/app-store.ts (1)
  • Feature (256-272)
libs/types/src/feature.ts (1)
  • Feature (24-58)
apps/ui/src/components/views/graph-view/utils/ancestor-context.ts (3)
  • AncestorContext (3-10)
  • getAncestors (21-57)
  • formatAncestorContextForPrompt (66-93)
apps/ui/src/components/views/board-view/shared/ancestor-context-section.tsx (1)
  • AncestorContextSection (25-201)
🔇 Additional comments (46)
apps/ui/package.json (1)

47-47: LGTM!

The @radix-ui/react-collapsible dependency is appropriate for the ancestor context collapsible UI. The version range follows the same pattern as other Radix packages in this project.

apps/ui/src/components/views/board-view/hooks/use-board-actions.ts (2)

101-101: LGTM!

The optional dependencies parameter with appropriate default handling enables the spawn sub-task feature to pass dependency relationships through the feature creation flow.


154-154: LGTM!

Correctly defaults to an empty array when dependencies aren't provided, ensuring consistent data shape.

apps/ui/src/components/views/graph-view/utils/ancestor-context.ts (2)

21-57: LGTM!

The ancestor traversal implementation is well-designed:

  • Uses a visited Set to prevent cycles
  • maxDepth parameter guards against runaway recursion
  • featureMap provides O(1) lookups
  • Sorting by depth ensures closest ancestors appear first

66-93: LGTM!

Clean markdown generation with sensible fallback for missing titles. The structured output format will work well for inclusion in task prompts.

apps/ui/src/components/views/board-view/shared/index.ts (1)

9-9: LGTM!

Standard barrel export addition for the new AncestorContextSection component.

apps/ui/src/components/views/graph-view/components/task-node.tsx (1)

270-279: LGTM!

The "Spawn Sub-Task" menu item follows the established pattern of other dropdown actions. The GitFork icon is semantically appropriate, and the event handling with stopPropagation is correct.

apps/ui/src/components/views/graph-view/utils/index.ts (1)

1-2: LGTM!

Clean barrel file establishing the graph-view utilities export surface.

apps/ui/src/components/views/board-view/kanban-board.tsx (2)

44-44: LGTM!

The optional onSpawnTask callback prop follows the established pattern for action handlers in this component.


189-189: LGTM!

Correctly wraps the callback to pass the feature context, with optional chaining for safe invocation when the prop isn't provided.

apps/ui/src/components/views/graph-view/graph-canvas.tsx (4)

14-14: LGTM!

Connection type import is correctly added to support the new edge connection handling.


57-57: LGTM!

The onCreateDependency prop is well-designed with an optional callback pattern that returns a Promise to indicate success/failure.

Also applies to: 69-69


144-155: LGTM!

The edge connection handler is well-implemented with proper validation and clear documentation of React Flow's source/target semantics.


184-184: LGTM!

The handleConnect callback is correctly wired to ReactFlow's onConnect prop, completing the dependency creation flow.

apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx (1)

28-28: LGTM!

The onSpawnTask callback is correctly threaded through KanbanCard to CardHeaderSection, consistent with the existing prop pattern.

Also applies to: 55-55, 151-151

apps/ui/src/components/views/graph-view/hooks/use-graph-nodes.ts (1)

27-27: LGTM!

The onSpawnTask callback follows the established pattern for other action callbacks with proper feature ID binding.

Also applies to: 44-44, 117-119

apps/ui/src/components/views/board-view.tsx (5)

110-111: LGTM!

State variable for tracking the parent feature during spawn operations is appropriately named and initialized.


1027-1030: LGTM!

Both KanbanBoard and GraphView spawn handlers correctly set the parent feature and open the add dialog for a unified spawn experience.

Also applies to: 1056-1059


1094-1099: LGTM!

Clearing the spawn parent feature when the dialog closes prevents stale state and ensures clean spawn operations.


1110-1111: LGTM!

Passing parentFeature and allFeatures to AddFeatureDialog enables the ancestor context selection UI for spawned tasks.


1053-1055: Verify that handleUpdateFeature persists changes to the backend.

The onUpdateFeature callback is essential for dependency creation to work correctly. Ensure that handleUpdateFeature (from useBoardActions) properly persists feature updates to the backend and does not only update local state.

apps/ui/src/components/views/board-view/shared/ancestor-context-section.tsx (5)

25-61: LGTM!

The event handlers for toggling selection and expansion are correctly implemented using Set operations for efficient state management.


62-68: LGTM!

The combined list construction correctly marks the parent with depth -1 and isParent true, while ancestors use their own depth values. This enables proper indentation rendering.


116-118: Fallback logic for displayTitle is well-designed.

The title fallback gracefully handles missing titles by truncating the description, ensuring the UI always displays meaningful text.


127-127: Dynamic indentation calculation is correct.

Using item.depth * 12 for ancestor indentation while parent has 0 margin creates a clear visual hierarchy.


193-197: LGTM!

The empty state message is appropriate and informs users when there are no ancestors beyond the parent.

apps/ui/src/components/views/graph-view/utils/dependency-validation.ts (2)

12-37: LGTM!

The circular dependency detection correctly uses DFS to check if adding targetId → sourceId would create a cycle by verifying whether sourceId can already reach targetId through existing dependencies. The visited set prevents infinite loops in existing cycles.


47-51: LGTM!

The dependency existence check is straightforward and correctly verifies whether targetId already depends on sourceId.

apps/ui/src/components/views/graph-view/graph-view.tsx (4)

6-7: LGTM!

Importing the validation utilities and toast notification library is appropriate for the dependency creation flow.


22-23: LGTM!

The new props onUpdateFeature and onSpawnTask extend GraphView's capabilities correctly for dependency management and task spawning.

Also applies to: 39-40


159-164: LGTM!

The onSpawnTask callback is correctly integrated into nodeActionCallbacks and the dependency array, enabling task spawning from graph nodes.

Also applies to: 166-166


178-178: LGTM!

Wiring handleCreateDependency to GraphCanvas completes the end-to-end dependency creation flow via edge dragging.

apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx (5)

22-22: LGTM!

GitFork icon import and onSpawnTask prop are correctly added to support the spawn sub-task functionality.

Also applies to: 35-35, 45-45


98-108: LGTM!

Spawn Sub-Task action properly integrated into the running task dropdown with appropriate event handling and test ID.


123-137: LGTM!

The backlog header correctly accommodates the new spawn button alongside the delete button, with proper event propagation control and accessibility attributes (title, data-testid).


172-187: LGTM!

Spawn button properly integrated into waiting_approval/verified header with dynamic test IDs based on status.


276-286: LGTM!

Spawn Sub-Task action correctly added to the in-progress dropdown menu, maintaining consistency with other status states.

apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx (9)

86-89: LGTM!

The spawn mode props are well-typed with sensible defaults. The optional nature allows backward compatibility with existing callers.


104-107: LGTM!

Clean derivation of isSpawnMode from the presence of parentFeature.


135-138: LGTM!

Using Set<string> for selectedAncestorIds provides efficient membership checks. State initialization is appropriate.


167-190: LGTM!

The ancestor initialization logic correctly computes and pre-selects all ancestors when spawning. Default selection of all ancestors is good UX.

Note: Ensure allFeatures has a stable reference (e.g., from store state) to avoid unnecessary effect re-runs if this array is recreated on each render.


215-237: LGTM!

The description composition logic correctly:

  1. Creates a parent context object for uniform handling
  2. Combines with computed ancestors
  3. Only prepends context when selections exist
  4. Uses a clear markdown structure with separators

253-255: LGTM!

Correctly adds only the parent as a dependency in spawn mode. The ancestor selections provide context (prepended to description) while the parent establishes the actual dependency relationship.


353-358: LGTM!

The dialog header provides clear context about the spawn operation, including a preview of the parent task. The truncation at 50 characters keeps the dialog description readable.


378-392: LGTM!

The AncestorContextSection is correctly conditionally rendered and receives properly shaped props. Extracting only the needed properties from parentFeature is a clean approach.


586-586: LGTM!

Button text appropriately reflects the action context.

@Shironex Shironex added Enhancement Improvements to existing functionality or UI. Do Not Merge Use this label if something should not be merged. labels Dec 23, 2025
JBotwina and others added 2 commits December 23, 2025 19:30
- Add edge dragging to create dependencies in graph view
- Add spawn sub-task action available in graph view and kanban board
- Implement ancestor context selection when spawning tasks
- Add dependency validation (circular, self, duplicate prevention)
- Include ancestor context in spawned task descriptions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Consolidated dependency validation and ancestor traversal utilities:
- wouldCreateCircularDependency, dependencyExists -> @automaker/dependency-resolver
- getAncestors, formatAncestorContextForPrompt, AncestorContext -> @automaker/dependency-resolver
- Removed graph-view/utils directory (now redundant)
- Updated all imports to use shared package

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@JBotwina JBotwina force-pushed the JBotwina/task-deps-spawn branch from 8c1322d to 76b7cfe Compare December 24, 2025 00:30
…nhancements

- Added onDeleteTask functionality to allow task deletion from both board and graph views.
- Integrated delete options for dependencies in the graph view, enhancing user interaction.
- Updated ancestor context section to clarify the role of parent tasks in task descriptions.
- Improved layout handling in graph view to preserve node positions during updates.

This update enhances task management capabilities and improves user experience in the graph view.
@JBotwina JBotwina marked this pull request as ready for review December 24, 2025 01:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/ui/src/components/views/graph-view/graph-view.tsx (1)

85-130: Dependencies may not persist to backend - verify integration.

A previous review flagged that onUpdateFeature is bound to handleUpdateFeature in use-board-actions.ts, which has a narrowed type signature that may exclude dependencies. Ensure the handler properly accepts and persists the dependencies field, or this feature won't work as expected.

#!/bin/bash
# Verify handleUpdateFeature accepts dependencies in its update shape
rg -nP "handleUpdateFeature" apps/ui/src/components/views/board-view/hooks/use-board-actions.ts -A 20 | head -50
apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx (1)

352-357: Consider using title with fallback instead of truncated description.

The dialog subtitle truncates the description mid-word with parentFeature?.description.slice(0, 50). A previous review suggested using parentFeature.title as the primary display with description as fallback.

🔎 Proposed improvement
-              ? `Create a sub-task that depends on "${parentFeature?.title || parentFeature?.description.slice(0, 50)}..."`
+              ? `Create a sub-task that depends on "${parentFeature?.title || (parentFeature?.description?.slice(0, 50) + '...')}"`

Or better, use a utility function that truncates at word boundaries.

🧹 Nitpick comments (6)
apps/ui/src/components/views/graph-view/graph-canvas.tsx (1)

131-151: Consider consolidating the duplicate position preservation logic.

Lines 131-139 and 143-149 contain identical logic. While separating them documents the different intents, you could consolidate:

🔎 Optional refactor to reduce duplication
-    } else if (hasNewNodes) {
-      // New nodes added - need to re-layout but try to preserve existing positions
-      setNodes((currentNodes) => {
-        const positionMap = new Map(currentNodes.map((n) => [n.id, n.position]));
-        return layoutedNodes.map((node) => ({
-          ...node,
-          position: positionMap.get(node.id) || node.position,
-        }));
-      });
-      setEdges(layoutedEdges);
-    } else {
-      // No new nodes - just update data without changing positions
+    } else {
+      // Preserve existing positions, use layout position for new nodes
       setNodes((currentNodes) => {
         const positionMap = new Map(currentNodes.map((n) => [n.id, n.position]));
         return layoutedNodes.map((node) => ({
           ...node,
           position: positionMap.get(node.id) || node.position,
         }));
       });
-      // Update edges without triggering re-render of nodes
       setEdges(layoutedEdges);
     }
libs/dependency-resolver/src/resolver.ts (1)

287-323: Consider clarifying depth semantics in documentation.

The depth value represents the traversal depth starting from 0 for immediate parents. However, formatAncestorContextForPrompt uses depth: -1 as a special marker for the parent task (see lines 341-343). This convention mismatch could be confusing since getAncestors never returns depth: -1.

Consider documenting that depth: -1 is a sentinel value only used when manually constructing AncestorContext for parent tasks outside of getAncestors.

apps/ui/src/components/views/graph-view/graph-view.tsx (1)

119-119: Unnecessary type assertion on dependencies.

The cast as string[] | undefined is redundant if the Feature type already defines dependencies as string[]. If the type is correct upstream, this cast can be removed.

🔎 Proposed simplification
-      const currentDeps = (targetFeature.dependencies as string[] | undefined) || [];
+      const currentDeps = targetFeature.dependencies ?? [];
apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx (1)

167-177: Ancestors fetched for parent but parent itself isn't an ancestor.

The getAncestors(parentFeature, allFeatures) call retrieves ancestors of the parent feature (i.e., grandparents and beyond), not the parent itself. Later in handleAdd (lines 218-231), a parentContext is manually constructed with depth: -1 and merged with ancestors. This is correct behavior, but the state variable name ancestors could be clearer (e.g., parentAncestors or ancestorsOfParent).

apps/ui/src/components/ui/collapsible.tsx (1)

1-1: Optional: Remove unused React import.

The React import is not used in this file since there's no JSX or React API usage. Consider removing it for clarity.

🔎 Proposed fix
-import * as React from 'react';
 import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
apps/ui/src/components/views/graph-view/hooks/use-graph-layout.ts (1)

115-125: Optional: remove unnecessary dependency.

The getLayoutedElements callback has an empty dependency array (line 71), making it stable across renders. Including it in the runLayout dependency array (line 124) is unnecessary, though harmless.

🔎 Proposed cleanup
     [nodes, edges, getLayoutedElements, setNodes, fitView]
+    // getLayoutedElements has empty deps and is stable, can be omitted
+    [nodes, edges, setNodes, fitView]
   );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f780f53 and 34c0d39.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • apps/ui/package.json
  • apps/ui/src/components/ui/collapsible.tsx
  • apps/ui/src/components/views/board-view.tsx
  • apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx
  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
  • apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx
  • apps/ui/src/components/views/board-view/hooks/use-board-actions.ts
  • apps/ui/src/components/views/board-view/kanban-board.tsx
  • apps/ui/src/components/views/board-view/shared/ancestor-context-section.tsx
  • apps/ui/src/components/views/board-view/shared/index.ts
  • apps/ui/src/components/views/graph-view/components/dependency-edge.tsx
  • apps/ui/src/components/views/graph-view/components/task-node.tsx
  • apps/ui/src/components/views/graph-view/graph-canvas.tsx
  • apps/ui/src/components/views/graph-view/graph-view.tsx
  • apps/ui/src/components/views/graph-view/hooks/use-graph-layout.ts
  • apps/ui/src/components/views/graph-view/hooks/use-graph-nodes.ts
  • libs/dependency-resolver/src/index.ts
  • libs/dependency-resolver/src/resolver.ts
  • libs/dependency-resolver/tests/resolver.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/ui/package.json
  • apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx
  • apps/ui/src/components/views/board-view/shared/ancestor-context-section.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
libs/dependency-resolver/src/resolver.ts (1)
libs/dependency-resolver/src/index.ts (5)
  • wouldCreateCircularDependency (10-10)
  • dependencyExists (11-11)
  • AncestorContext (15-15)
  • getAncestors (12-12)
  • formatAncestorContextForPrompt (13-13)
libs/dependency-resolver/tests/resolver.test.ts (1)
libs/dependency-resolver/src/resolver.ts (2)
  • wouldCreateCircularDependency (223-250)
  • dependencyExists (260-264)
apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx (1)
apps/ui/src/components/ui/button.tsx (1)
  • Button (108-108)
apps/ui/src/components/views/graph-view/components/dependency-edge.tsx (2)
apps/ui/src/store/app-store.ts (1)
  • Feature (256-272)
apps/ui/src/lib/utils.ts (1)
  • cn (5-7)
🔇 Additional comments (35)
apps/ui/src/components/views/board-view/shared/index.ts (1)

9-9: LGTM!

The new re-export follows the existing pattern and correctly exposes the AncestorContextSection component through the shared module's public API.

apps/ui/src/components/views/board-view/hooks/use-board-actions.ts (3)

101-101: LGTM!

Clean addition of the optional dependencies field to the feature creation parameter, enabling dependency tracking from the point of feature creation.


148-155: LGTM!

Good defensive approach providing an empty array default for dependencies when not specified, ensuring the created feature always has a valid array for the dependencies field.


371-386: Soft blocking with toast warning is a good UX choice.

The implementation allows starting features with incomplete dependencies while informing the user. This provides flexibility without being overly restrictive.

apps/ui/src/components/views/graph-view/components/dependency-edge.tsx (2)

82-91: Good UX pattern with the invisible hover path.

The wider transparent stroke (20px) makes edge hover detection much more user-friendly, especially for thin edges that would otherwise be difficult to target.


128-157: LGTM!

The delete button implementation is well-structured:

  • Appears on hover or selection, providing clear affordance
  • Uses EdgeLabelRenderer correctly for proper positioning
  • Maintains hover state when interacting with the button itself (lines 138-139)
  • Proper z-index ensures visibility
apps/ui/src/components/views/board-view/kanban-board.tsx (2)

44-44: LGTM!

The optional onSpawnTask callback follows the established pattern of other action callbacks in KanbanBoardProps.


189-189: LGTM!

Correctly wraps the callback to pass the feature context while using optional chaining to safely handle the undefined case.

libs/dependency-resolver/tests/resolver.test.ts (2)

354-496: Excellent test coverage for wouldCreateCircularDependency.

The tests comprehensively cover:

  • Base cases (no deps, valid chains)
  • Direct and transitive cycles
  • Complex patterns (diamond, parallel branches)
  • Edge cases (self-dependency, missing features, empty list)

The parallel branches test (lines 413-426) is particularly valuable to ensure no false positives.


498-552: Good coverage for dependencyExists.

The test at lines 518-528 correctly validates that this function only checks direct dependencies, not transitive ones—important for clarity on the function's contract.

apps/ui/src/components/views/graph-view/components/task-node.tsx (2)

94-105: LGTM!

Adding explicit id="target" and isConnectable={true} enables proper edge connection targeting. The styling with hover feedback provides good visual affordance.


273-294: Good safety guard on Delete Task.

The !data.isRunning condition (line 283) prevents accidental deletion of actively running tasks, which could cause issues with orphaned processes.

apps/ui/src/components/views/board-view.tsx (4)

110-111: LGTM!

Clean state introduction for tracking the parent feature during spawn mode.


1027-1030: LGTM!

The spawn task handler correctly sets the parent feature context before opening the dialog, enabling the AddFeatureDialog to pre-configure the new task with the dependency relationship.


1093-1098: Current implementation is correct.

Clearing spawnParentFeature on dialog close is sufficient. When the dialog opens via the normal "Add Feature" flow (line 937), spawnParentFeature is either already null or was cleared from a previous close. The past review suggestion to add an else branch is unnecessary complexity.


1053-1058: LGTM!

Good integration of both onSpawnTask and onDeleteTask handlers for the GraphView, completing the feature parity between Kanban and Graph views for task management actions.

apps/ui/src/components/views/graph-view/graph-canvas.tsx (2)

113-156: Good layout preservation strategy.

The three-branch approach (initial render, new nodes, no new nodes) correctly handles:

  • First render: apply full layout
  • New nodes added: layout new nodes while preserving user-positioned existing nodes
  • Data updates only: preserve all positions

The positionMap pattern elegantly handles the fallback to layout position for new nodes.


184-204: LGTM!

The connection handling is well-structured:

  • handleConnect delegates to onCreateDependency for validation and persistence
  • isValidConnection provides fast client-side rejection of self-connections
  • Validation (circular deps, duplicates) correctly happens in the upstream handler
libs/dependency-resolver/src/resolver.ts (3)

223-250: LGTM! Well-structured circular dependency detection.

The DFS-based reachability check correctly identifies cycles by checking if sourceId can reach targetId through existing dependencies before adding the new edge. The visited set properly prevents infinite loops in cyclic graphs.


260-264: LGTM! Simple and correct dependency existence check.


334-396: LGTM! Clear markdown formatting with appropriate separation of parent and ancestor contexts.

The function correctly handles the special depth: -1 case for parent tasks with emphasized formatting, and gracefully handles empty selections by returning an empty string.

libs/dependency-resolver/src/index.ts (1)

6-16: LGTM! Clean barrel export expansion.

All new utilities and the AncestorContext type are properly re-exported, maintaining a clean public API surface.

apps/ui/src/components/views/graph-view/graph-view.tsx (1)

165-190: LGTM! Callback implementations follow existing patterns.

The new onSpawnTask, onDeleteTask, and onDeleteDependency callbacks are correctly implemented with consistent feature lookup and optional chaining patterns.

apps/ui/src/components/views/graph-view/hooks/use-graph-nodes.ts (2)

27-28: LGTM! Interface extensions are consistent and well-typed.

The new optional callbacks (onSpawnTask, onDeleteTask, onDeleteDependency) follow the established pattern of the existing callbacks and are properly typed.

Also applies to: 46-48


121-126: LGTM! Callback binding follows established patterns.

The new callbacks are bound correctly using the same pattern as existing callbacks (checking existence before binding).

Also applies to: 156-156

apps/ui/src/components/views/board-view/dialogs/add-feature-dialog.tsx (3)

214-236: LGTM! Context construction logic is correct.

The parent is correctly added as a special AncestorContext with depth: -1, merged with ancestors, and formatted. The conditional check ensures context is only prepended when ancestors are selected.


252-253: LGTM! Parent automatically added as dependency in spawn mode.

This ensures spawned tasks correctly depend on their parent, which aligns with the PR objectives.


377-391: LGTM! AncestorContextSection properly wired.

The component receives all necessary props for displaying and selecting ancestor context.

apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx (2)

35-35: LGTM! New callback prop properly typed and destructured.

The optional onSpawnTask callback follows the same pattern as existing callbacks like onViewOutput.

Also applies to: 45-45


98-108: LGTM! Spawn Sub-Task action consistently implemented across all card states.

The spawn action is:

  • Present in running, backlog, waiting_approval/verified, and in_progress states
  • Uses GitFork icon consistently
  • Includes proper e.stopPropagation() handling
  • Has appropriate data-testid attributes for testing

Also applies to: 123-137, 172-187, 276-286

apps/ui/src/components/ui/collapsible.tsx (1)

2-2: The @radix-ui/react-collapsible dependency is current and secure.

The package is at version 1.1.12, which is the latest available, and there are no known security vulnerabilities.

apps/ui/src/components/views/graph-view/hooks/use-graph-layout.ts (4)

1-1: LGTM!

The addition of useRef to the imports is appropriate for the position caching functionality introduced in this file.


55-60: LGTM!

The position caching logic correctly computes node positions from dagre output and stores them in the cache for reuse.


85-112: The conditional layout logic is correctly implemented.

The caching optimization properly distinguishes between structural changes (nodes added/removed) and non-structural changes. When the structure is unchanged, cached positions are reused, avoiding expensive dagre recalculations.

Note: The edge-change re-layout concern has been flagged in a previous comment on lines 74-82.


74-82: Confirm design intent before deciding on edge-change re-layout behavior.

The current implementation intentionally excludes edges from structureKey to avoid expensive layout recalculations on every edge change—this is documented in the code comment (line 75). However, when users drag edges to create dependencies, the graph doesn't automatically reorganize to reflect the new hierarchy; they must manually click a layout button to trigger re-layout.

This creates a suboptimal workflow: create dependency → manually invoke layout. The control panel buttons (Horizontal/Vertical Layout) do provide a way to re-layout, and they are visible with tooltips, but the workflow isn't seamless.

Before changing this design, clarify:

  • Is the current manual re-layout workflow acceptable for the expected user behavior?
  • If users expect automatic re-layout on dependency creation, consider one of these options:
    1. Include edge structure in structureKey to auto-trigger re-layout (performance cost)
    2. Auto-trigger runLayout after successful dependency creation (debounced to batch multiple operations)
    3. Improve discoverability of the layout buttons via more prominent UI or automatic triggering on edge changes

The code is working as designed—verify whether the design itself meets product requirements.

Comment on lines +21 to +23
// Cache the last computed positions to avoid recalculating layout
const positionCache = useRef<Map<string, { x: number; y: number }>>(new Map());
const lastStructureKey = useRef<string>('');
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential memory leak: stale entries accumulate in positionCache.

When nodes are removed from the graph, their cached positions remain in positionCache indefinitely. The cache is only cleared when nodes.length === 0 (line 87), but removed nodes' entries are never pruned. Over time, if nodes are frequently added and removed, the cache will grow unbounded.

🔎 Proposed fix to clean up stale cache entries

After line 23, add a cleanup effect or prune stale entries when processing the layout. For example, add this after computing layoutedElements:

   }, [nodes, edges, structureKey, getLayoutedElements]);
+
+  // Clean up stale cache entries
+  useMemo(() => {
+    const currentNodeIds = new Set(nodes.map(n => n.id));
+    for (const cachedId of positionCache.current.keys()) {
+      if (!currentNodeIds.has(cachedId)) {
+        positionCache.current.delete(cachedId);
+      }
+    }
+  }, [nodes]);

Alternatively, prune inside the layoutedElements useMemo logic when structure changes (around line 97).

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
apps/ui/src/components/views/graph-view/hooks/use-graph-layout.ts lines 21-23:
positionCache (a Map held in a ref) accumulates entries for nodes that have been
removed; prune stale entries whenever the node set changes by removing map keys
not present in the current nodes list (or clear the map when nodes.length ===
0). Implement this by adding a small cleanup: on nodes change (useEffect with
nodes as dependency) iterate over positionCache.current keys and delete any key
not found in the current nodes' ids (or clear entirely if nodes is empty);
alternatively perform the same pruning at the start of the layoutedElements
useMemo so the cache never grows with removed node ids. Ensure you mutate
positionCache.current (delete keys) rather than reassigning the ref.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JBotwina can you check if this is a real issue and fix in another pr?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Do Not Merge Use this label if something should not be merged. Enhancement Improvements to existing functionality or UI.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants