@@ -270,6 +270,17 @@ export function BoardView() {
270270 fetchBranches ( ) ;
271271 } , [ currentProject , worktreeRefreshKey ] ) ;
272272
273+ // Calculate unarchived card counts per branch
274+ const branchCardCounts = useMemo ( ( ) => {
275+ return hookFeatures . reduce ( ( counts , feature ) => {
276+ if ( feature . status !== "completed" ) {
277+ const branch = feature . branchName ?? "main" ;
278+ counts [ branch ] = ( counts [ branch ] || 0 ) + 1 ;
279+ }
280+ return counts ;
281+ } , { } as Record < string , number > ) ;
282+ } , [ hookFeatures ] ) ;
283+
273284 // Custom collision detection that prioritizes columns over cards
274285 const collisionDetectionStrategy = useCallback ( ( args : any ) => {
275286 // First, check if pointer is within a column
@@ -302,14 +313,14 @@ export function BoardView() {
302313 } ) ;
303314
304315 if ( matchesRemovedWorktree ) {
305- // Reset the feature's branch assignment
306- persistFeatureUpdate ( feature . id , {
307- branchName : null as unknown as string | undefined ,
308- } ) ;
316+ // Reset the feature's branch assignment - update both local state and persist
317+ const updates = { branchName : null as unknown as string | undefined } ;
318+ updateFeature ( feature . id , updates ) ;
319+ persistFeatureUpdate ( feature . id , updates ) ;
309320 }
310321 } ) ;
311322 } ,
312- [ hookFeatures , persistFeatureUpdate ]
323+ [ hookFeatures , updateFeature , persistFeatureUpdate ]
313324 ) ;
314325
315326 // Get in-progress features for keyboard shortcuts (needed before actions hook)
@@ -418,6 +429,18 @@ export function BoardView() {
418429 hookFeaturesRef . current = hookFeatures ;
419430 } , [ hookFeatures ] ) ;
420431
432+ // Use a ref to track running tasks to avoid effect re-runs that clear pendingFeaturesRef
433+ const runningAutoTasksRef = useRef ( runningAutoTasks ) ;
434+ useEffect ( ( ) => {
435+ runningAutoTasksRef . current = runningAutoTasks ;
436+ } , [ runningAutoTasks ] ) ;
437+
438+ // Keep latest start handler without retriggering the auto mode effect
439+ const handleStartImplementationRef = useRef ( handleStartImplementation ) ;
440+ useEffect ( ( ) => {
441+ handleStartImplementationRef . current = handleStartImplementation ;
442+ } , [ handleStartImplementation ] ) ;
443+
421444 // Track features that are pending (started but not yet confirmed running)
422445 const pendingFeaturesRef = useRef < Set < string > > ( new Set ( ) ) ;
423446
@@ -485,8 +508,9 @@ export function BoardView() {
485508 }
486509
487510 // Count currently running tasks + pending features
511+ // Use ref to get the latest running tasks without causing effect re-runs
488512 const currentRunning =
489- runningAutoTasks . length + pendingFeaturesRef . current . size ;
513+ runningAutoTasksRef . current . length + pendingFeaturesRef . current . size ;
490514 const availableSlots = maxConcurrency - currentRunning ;
491515
492516 // No available slots, skip check
@@ -541,6 +565,10 @@ export function BoardView() {
541565
542566 // Start features up to available slots
543567 const featuresToStart = eligibleFeatures . slice ( 0 , availableSlots ) ;
568+ const startImplementation = handleStartImplementationRef . current ;
569+ if ( ! startImplementation ) {
570+ return ;
571+ }
544572
545573 for ( const feature of featuresToStart ) {
546574 // Check again before starting each feature
@@ -566,7 +594,7 @@ export function BoardView() {
566594 }
567595
568596 // Start the implementation - server will derive workDir from feature.branchName
569- const started = await handleStartImplementation ( feature ) ;
597+ const started = await startImplementation ( feature ) ;
570598
571599 // If successfully started, track it as pending until we receive the start event
572600 if ( started ) {
@@ -580,7 +608,7 @@ export function BoardView() {
580608
581609 // Check immediately, then every 3 seconds
582610 checkAndStartFeatures ( ) ;
583- const interval = setInterval ( checkAndStartFeatures , 3000 ) ;
611+ const interval = setInterval ( checkAndStartFeatures , 1000 ) ;
584612
585613 return ( ) => {
586614 // Mark as inactive to prevent any pending async operations from continuing
@@ -592,7 +620,8 @@ export function BoardView() {
592620 } , [
593621 autoMode . isRunning ,
594622 currentProject ,
595- runningAutoTasks ,
623+ // runningAutoTasks is accessed via runningAutoTasksRef to prevent effect re-runs
624+ // that would clear pendingFeaturesRef and cause concurrency issues
596625 maxConcurrency ,
597626 // hookFeatures is accessed via hookFeaturesRef to prevent effect re-runs
598627 currentWorktreeBranch ,
@@ -601,7 +630,6 @@ export function BoardView() {
601630 isPrimaryWorktreeBranch ,
602631 enableDependencyBlocking ,
603632 persistFeatureUpdate ,
604- handleStartImplementation ,
605633 ] ) ;
606634
607635 // Use keyboard shortcuts hook (after actions hook)
@@ -640,7 +668,9 @@ export function BoardView() {
640668 // Find feature for pending plan approval
641669 const pendingApprovalFeature = useMemo ( ( ) => {
642670 if ( ! pendingPlanApproval ) return null ;
643- return hookFeatures . find ( ( f ) => f . id === pendingPlanApproval . featureId ) || null ;
671+ return (
672+ hookFeatures . find ( ( f ) => f . id === pendingPlanApproval . featureId ) || null
673+ ) ;
644674 } , [ pendingPlanApproval , hookFeatures ] ) ;
645675
646676 // Handle plan approval
@@ -666,10 +696,10 @@ export function BoardView() {
666696 if ( result . success ) {
667697 // Immediately update local feature state to hide "Approve Plan" button
668698 // Get current feature to preserve version
669- const currentFeature = hookFeatures . find ( f => f . id === featureId ) ;
699+ const currentFeature = hookFeatures . find ( ( f ) => f . id === featureId ) ;
670700 updateFeature ( featureId , {
671701 planSpec : {
672- status : ' approved' ,
702+ status : " approved" ,
673703 content : editedPlan || pendingPlanApproval . planContent ,
674704 version : currentFeature ?. planSpec ?. version || 1 ,
675705 approvedAt : new Date ( ) . toISOString ( ) ,
@@ -688,7 +718,14 @@ export function BoardView() {
688718 setPendingPlanApproval ( null ) ;
689719 }
690720 } ,
691- [ pendingPlanApproval , currentProject , setPendingPlanApproval , updateFeature , loadFeatures , hookFeatures ]
721+ [
722+ pendingPlanApproval ,
723+ currentProject ,
724+ setPendingPlanApproval ,
725+ updateFeature ,
726+ loadFeatures ,
727+ hookFeatures ,
728+ ]
692729 ) ;
693730
694731 // Handle plan rejection
@@ -715,11 +752,11 @@ export function BoardView() {
715752 if ( result . success ) {
716753 // Immediately update local feature state
717754 // Get current feature to preserve version
718- const currentFeature = hookFeatures . find ( f => f . id === featureId ) ;
755+ const currentFeature = hookFeatures . find ( ( f ) => f . id === featureId ) ;
719756 updateFeature ( featureId , {
720- status : ' backlog' ,
757+ status : " backlog" ,
721758 planSpec : {
722- status : ' rejected' ,
759+ status : " rejected" ,
723760 content : pendingPlanApproval . planContent ,
724761 version : currentFeature ?. planSpec ?. version || 1 ,
725762 reviewedByUser : true ,
@@ -737,7 +774,14 @@ export function BoardView() {
737774 setPendingPlanApproval ( null ) ;
738775 }
739776 } ,
740- [ pendingPlanApproval , currentProject , setPendingPlanApproval , updateFeature , loadFeatures , hookFeatures ]
777+ [
778+ pendingPlanApproval ,
779+ currentProject ,
780+ setPendingPlanApproval ,
781+ updateFeature ,
782+ loadFeatures ,
783+ hookFeatures ,
784+ ]
741785 ) ;
742786
743787 // Handle opening approval dialog from feature card button
@@ -748,7 +792,7 @@ export function BoardView() {
748792 // Determine the planning mode for approval (skip should never have a plan requiring approval)
749793 const mode = feature . planningMode ;
750794 const approvalMode : "lite" | "spec" | "full" =
751- mode === ' lite' || mode === ' spec' || mode === ' full' ? mode : ' spec' ;
795+ mode === " lite" || mode === " spec" || mode === " full" ? mode : " spec" ;
752796
753797 // Re-open the approval dialog with the feature's plan data
754798 setPendingPlanApproval ( {
@@ -833,6 +877,7 @@ export function BoardView() {
833877 } }
834878 onRemovedWorktrees = { handleRemovedWorktrees }
835879 runningFeatureIds = { runningAutoTasks }
880+ branchCardCounts = { branchCardCounts }
836881 features = { hookFeatures . map ( ( f ) => ( {
837882 id : f . id ,
838883 branchName : f . branchName ,
@@ -929,6 +974,7 @@ export function BoardView() {
929974 onAdd = { handleAddFeature }
930975 categorySuggestions = { categorySuggestions }
931976 branchSuggestions = { branchSuggestions }
977+ branchCardCounts = { branchCardCounts }
932978 defaultSkipTests = { defaultSkipTests }
933979 defaultBranch = { selectedWorktreeBranch }
934980 currentBranch = { currentWorktreeBranch || undefined }
@@ -944,6 +990,7 @@ export function BoardView() {
944990 onUpdate = { handleUpdateFeature }
945991 categorySuggestions = { categorySuggestions }
946992 branchSuggestions = { branchSuggestions }
993+ branchCardCounts = { branchCardCounts }
947994 currentBranch = { currentWorktreeBranch || undefined }
948995 isMaximized = { isMaximized }
949996 showProfilesOnly = { showProfilesOnly }
@@ -1065,15 +1112,24 @@ export function BoardView() {
10651112 onOpenChange = { setShowDeleteWorktreeDialog }
10661113 projectPath = { currentProject . path }
10671114 worktree = { selectedWorktreeForAction }
1115+ affectedFeatureCount = {
1116+ selectedWorktreeForAction
1117+ ? hookFeatures . filter (
1118+ ( f ) => f . branchName === selectedWorktreeForAction . branch
1119+ ) . length
1120+ : 0
1121+ }
10681122 onDeleted = { ( deletedWorktree , _deletedBranch ) => {
10691123 // Reset features that were assigned to the deleted worktree (by branch)
10701124 hookFeatures . forEach ( ( feature ) => {
10711125 // Match by branch name since worktreePath is no longer stored
10721126 if ( feature . branchName === deletedWorktree . branch ) {
1073- // Reset the feature's branch assignment
1074- persistFeatureUpdate ( feature . id , {
1127+ // Reset the feature's branch assignment - update both local state and persist
1128+ const updates = {
10751129 branchName : null as unknown as string | undefined ,
1076- } ) ;
1130+ } ;
1131+ updateFeature ( feature . id , updates ) ;
1132+ persistFeatureUpdate ( feature . id , updates ) ;
10771133 }
10781134 } ) ;
10791135
0 commit comments