diff --git a/apps/server/src/services/spec-parser.ts b/apps/server/src/services/spec-parser.ts
index 295246fb..811ed925 100644
--- a/apps/server/src/services/spec-parser.ts
+++ b/apps/server/src/services/spec-parser.ts
@@ -214,7 +214,8 @@ export function extractSummary(text: string): string | null {
}
// Check for ## Summary section (use last match)
- // Use \n## [^#] to stop at same-level headers (## Foo) but NOT subsections (### Root Cause)
+ // Stop at \n## [^#] (same-level headers like "## Changes") but preserve ### subsections
+ // (like "### Root Cause", "### Fix Applied") that belong to the summary content.
const sectionMatches = text.matchAll(/##\s*Summary\s*\n+([\s\S]*?)(?=\n## [^#]|\n\*\*|$)/gi);
const sectionMatch = getLastMatch(sectionMatches);
if (sectionMatch) {
diff --git a/apps/ui/src/components/views/board-view.tsx b/apps/ui/src/components/views/board-view.tsx
index f0c92784..ecbbe8a7 100644
--- a/apps/ui/src/components/views/board-view.tsx
+++ b/apps/ui/src/components/views/board-view.tsx
@@ -83,7 +83,7 @@ import type {
StashApplyConflictInfo,
} from './board-view/worktree-panel/types';
import { BoardErrorBoundary } from './board-view/board-error-boundary';
-import { COLUMNS, getColumnsWithPipeline } from './board-view/constants';
+import { COLUMNS, getColumnsWithPipeline, isBacklogLikeStatus } from './board-view/constants';
import {
useBoardFeatures,
useBoardDragDrop,
@@ -1908,12 +1908,7 @@ export function BoardView({ initialFeatureId }: BoardViewProps) {
// Running features should always show logs, even if status is
// stale (still 'backlog'/'ready'/'interrupted' during race window)
const isRunning = runningAutoTasksAllWorktrees.includes(feature.id);
- const isBacklogLike =
- feature.status === 'backlog' ||
- feature.status === 'merge_conflict' ||
- feature.status === 'ready' ||
- feature.status === 'interrupted';
- if (isBacklogLike && !isRunning) {
+ if (isBacklogLikeStatus(feature.status) && !isRunning) {
setEditingFeature(feature);
} else {
handleViewOutput(feature);
diff --git a/apps/ui/src/components/views/board-view/components/list-view/row-actions.tsx b/apps/ui/src/components/views/board-view/components/list-view/row-actions.tsx
index 173e014b..755ed017 100644
--- a/apps/ui/src/components/views/board-view/components/list-view/row-actions.tsx
+++ b/apps/ui/src/components/views/board-view/components/list-view/row-actions.tsx
@@ -30,6 +30,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import type { Feature } from '@/store/app-store';
+import { isBacklogLikeStatus } from '../../constants';
/**
* Action handler types for row actions
@@ -431,44 +432,41 @@ export const RowActions = memo(function RowActions({
>
)}
- {/* Running task with stale status (backlog/ready/interrupted but tracked as running).
+ {/* Running task with stale status - the feature is tracked as running but its
+ persisted status hasn't caught up yet during WebSocket/cache sync delays.
These features are placed in the in_progress column by useBoardColumnFeatures
- but their actual status hasn't updated yet, so no other menu block matches. */}
- {!isCurrentAutoTask &&
- isRunningTask &&
- (feature.status === 'backlog' ||
- feature.status === 'ready' ||
- feature.status === 'interrupted' ||
- feature.status === 'merge_conflict') && (
- <>
- {handlers.onViewOutput && (
+ (hooks/use-board-column-features.ts) but no other menu block matches their
+ stale status, so we provide running-appropriate actions here. */}
+ {!isCurrentAutoTask && isRunningTask && isBacklogLikeStatus(feature.status) && (
+ <>
+ {handlers.onViewOutput && (
+
+ )}
+
+ {handlers.onSpawnTask && (
+
+ )}
+ {handlers.onForceStop && (
+ <>
+
- )}
-
- {handlers.onSpawnTask && (
-
- )}
- {handlers.onForceStop && (
- <>
-
-
- >
- )}
- >
- )}
+ >
+ )}
+ >
+ )}
{/* Backlog actions */}
{!isCurrentAutoTask &&
diff --git a/apps/ui/src/components/views/board-view/constants.ts b/apps/ui/src/components/views/board-view/constants.ts
index fda19ebf..8ec071d8 100644
--- a/apps/ui/src/components/views/board-view/constants.ts
+++ b/apps/ui/src/components/views/board-view/constants.ts
@@ -136,6 +136,26 @@ export function getPipelineInsertIndex(): number {
return BASE_COLUMNS.length;
}
+/**
+ * Statuses that display in the backlog column because they don't have dedicated columns:
+ * - 'backlog': Default state for new features
+ * - 'ready': Feature has an approved plan, waiting for execution
+ * - 'interrupted': Feature execution was aborted (user stopped it, server restart)
+ * - 'merge_conflict': Automatic merge failed, user must resolve conflicts
+ *
+ * Used to determine row click behavior and menu actions when a feature is running
+ * but its status hasn't updated yet (race condition during WebSocket/cache sync).
+ * See use-board-column-features.ts for the column assignment logic.
+ */
+export function isBacklogLikeStatus(status: string): boolean {
+ return (
+ status === 'backlog' ||
+ status === 'ready' ||
+ status === 'interrupted' ||
+ status === 'merge_conflict'
+ );
+}
+
/**
* Check if a status is a pipeline status
*/