Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
fac8424
fix(frontend): prevent false stuck detection for ai_review tasks
AndyMik90 Dec 26, 2025
bd38e0a
ci(beta-release): use tag-based versioning instead of modifying packaโ€ฆ
AndyMik90 Dec 26, 2025
e1a7f91
fix(ollama): add packaged app path resolution for Ollama detector script
AndyMik90 Dec 26, 2025
4ae6a01
refactor(paths): remove legacy auto-claude path fallbacks
AndyMik90 Dec 26, 2025
d85c4e2
fix(frontend): address PR review feedback from Auto Claude and bots
AndyMik90 Dec 26, 2025
16074cb
Merge remote-tracking branch 'origin/develop' into fix/2.7.2-fixes
AndyMik90 Dec 26, 2025
2e14f70
fix(frontend): validate empty profile slug after sanitization
AndyMik90 Dec 26, 2025
7ba1286
auto-claude: subtask-1-2 - Verify macOS build process bundles all depโ€ฆ
AndyMik90 Dec 27, 2025
3939d79
fix: add z-10 to dialog close button to fix click handling (#379)
AndyMik90 Dec 28, 2025
9bbb73b
fix(ui): show add project modal instead of opening file explorer direโ€ฆ
AndyMik90 Dec 28, 2025
021db54
feat(agent): add project setting to include CLAUDE.md in agent context
AndyMik90 Dec 28, 2025
b579fbf
fix(security): expand Rust toolchain commands in allowlist
AndyMik90 Dec 28, 2025
287d7c7
refactor(python-env-manager): enhance dependency checks in checkDepsIโ€ฆ
AndyMik90 Dec 28, 2025
383f51b
fix(dialog): close new task modal with X button
AndyMik90 Dec 28, 2025
676417d
fix(terminal): allow project switching shortcuts when terminal focused
AndyMik90 Dec 28, 2025
507c66d
feat(deps): bundle Python packages at build time for instant app launch
AndyMik90 Dec 28, 2025
604a954
feat(ui): add responsive terminal title width based on terminal count
AndyMik90 Dec 28, 2025
cbaed27
fix(ui): remove broken terminal buttons from task review
AndyMik90 Dec 28, 2025
2a39ff9
feat(ollama): add qwen3 embedding models with global download progress
AndyMik90 Dec 28, 2025
e0e54df
fix(startup): auto-migrate stale autoBuildPath from old project strucโ€ฆ
AndyMik90 Dec 28, 2025
25412c9
feat(agents): add phase-aware MCP server configuration and MCP Overviโ€ฆ
AndyMik90 Dec 28, 2025
a4f4008
merge: resolve conflicts with origin/develop
AndyMik90 Dec 28, 2025
9637ead
feat(devtools): comprehensive IDE/terminal detection and configuration
AndyMik90 Dec 28, 2025
e42aecc
fix(github): expand AI bot detection patterns for PR reviews
AndyMik90 Dec 28, 2025
1c7e356
fix(security): address PR review security issues and code quality
AndyMik90 Dec 28, 2025
de2f4e5
fix(reliability): add error recovery for file writes and process tracโ€ฆ
AndyMik90 Dec 28, 2025
2bbba8f
fix(cleanup): remove unused function and fix race condition
AndyMik90 Dec 28, 2025
8b34f5f
fix(tests): guard app.on for test environments
AndyMik90 Dec 28, 2025
322d38c
fix(codeql): address remaining security alerts and code quality issues
AndyMik90 Dec 28, 2025
037cc2f
fix(ci): run tests on all Python versions, not just 3.13
AndyMik90 Dec 28, 2025
aa41944
fix(codeql): address remaining false positives with proper patterns
AndyMik90 Dec 28, 2025
d3b0ccf
fix(tests): add sleep to cache invalidation test for CI stability
AndyMik90 Dec 28, 2025
0b931fa
fix(codeql): avoid authentication keyword in test query string
AndyMik90 Dec 28, 2025
55b9019
fix(codeql): remove all auth-related terms from test data
AndyMik90 Dec 28, 2025
5359795
Merge branch 'develop' into fix/2.7.2-fixes
AndyMik90 Dec 28, 2025
afa28c8
fix(github): fetch PR reviews in followup review to capture Cursor/Coโ€ฆ
AndyMik90 Dec 28, 2025
418e0c4
fix(github): handle timezone-naive datetime in get_reviews_since
AndyMik90 Dec 28, 2025
f7cb1a6
feat(release): separate stable and beta download sections in README
AndyMik90 Dec 28, 2025
02c1d0b
fix(review): address Cursor and CodeRabbit review feedback
AndyMik90 Dec 28, 2025
38c8cf5
fix(tools): only allow auto-claude tools when MCP server is available
AndyMik90 Dec 29, 2025
9a91411
Merge branch 'develop' into fix/2.7.2-fixes
AlexMadera Dec 29, 2025
9c56f4b
fix(cross-platform): add Windows support for Python paths and CLI detโ€ฆ
AndyMik90 Dec 29, 2025
b8b1ad6
fix(tests): increase sleep time for reliable mtime detection on CI
AndyMik90 Dec 29, 2025
6415aa3
feat(ci): replace DCO with one-time CLA for contributions
AndyMik90 Dec 29, 2025
4b00aa5
fix(worktree): respect task-level branch override when creating worktโ€ฆ
AndyMik90 Dec 29, 2025
7174747
feat: add queue system with configurable parallel task limit
JoshuaRileyDev Dec 27, 2025
0f11979
feat(queue): implement full queue system v2 with auto-promotion
JoshuaRileyDev Dec 29, 2025
06cade3
feat(queue): add batch queue button to move all Planning tasks to Queue
JoshuaRileyDev Dec 29, 2025
e44733f
refactor(queue): change batch queue icon to ListPlus and move left
JoshuaRileyDev Dec 29, 2025
5ddc2fb
fix(queue): process multiple tasks to fill capacity after bulk queue
JoshuaRileyDev Dec 29, 2025
57ecb55
feat(queue): auto-promote from queue when task is stopped
JoshuaRileyDev Dec 29, 2025
dc28a6c
fix(docs): hyphenate 'open-source' as compound adjective in CLA
JoshuaRileyDev Dec 29, 2025
047366b
fix(security): address CodeRabbit review comments
JoshuaRileyDev Dec 29, 2025
13f8043
Merge upstream/develop into feature/queue-system-v2
JoshuaRileyDev Dec 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLA.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Subject to the terms and conditions of this Agreement, You hereby grant to the P

You understand and agree that the Project Owner may, in the future, license the Project, including Your Contributions, under additional licenses beyond the current GNU Affero General Public License version 3.0 (AGPL-3.0). Such additional licenses may include commercial or enterprise licenses.

This provision ensures the Project has proper licensing flexibility should such licensing options be introduced in the future. The open source version of the Project will continue to be available under AGPL-3.0.
This provision ensures the Project has proper licensing flexibility should such licensing options be introduced in the future. The open-source version of the Project will continue to be available under AGPL-3.0.

## 5. Representations

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,6 @@ def _generate_summary(
lines.append("")

lines.append("---")
lines.append("_Generated by Auto Claude Orchestrating PR Reviewer (Opus 4.5)_")
lines.append("_Generated by Auto Claude Orchestrating PR Reviewer_")

return "\n".join(lines)
211 changes: 187 additions & 24 deletions apps/frontend/src/renderer/components/KanbanBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ import {
sortableKeyboardCoordinates,
verticalListSortingStrategy
} from '@dnd-kit/sortable';
import { Plus, Inbox, Loader2, Eye, CheckCircle2, Archive } from 'lucide-react';
import { Plus, Inbox, Loader2, Eye, CheckCircle2, Archive, Settings, ListPlus } from 'lucide-react';
import { ScrollArea } from './ui/scroll-area';
import { Button } from './ui/button';
import { Checkbox } from './ui/checkbox';
import { Label } from './ui/label';
import { TaskCard } from './TaskCard';
import { SortableTaskCard } from './SortableTaskCard';
import { QueueSettingsModal } from './QueueSettingsModal';
import { TASK_STATUS_COLUMNS, TASK_STATUS_LABELS } from '../../shared/constants';
import { cn } from '../lib/utils';
import { persistTaskStatus, archiveTasks } from '../stores/task-store';
import { persistTaskStatus, archiveTasks, useTaskStore } from '../stores/task-store';
import { updateProjectSettings } from '../stores/project-store';
import { useProjectStore } from '../stores/project-store';
import type { Task, TaskStatus } from '../../shared/types';

interface KanbanBoardProps {
Expand All @@ -43,6 +46,9 @@ interface DroppableColumnProps {
isOver: boolean;
onAddClick?: () => void;
onArchiveAll?: () => void;
onQueueSettings?: () => void;
onQueueAll?: () => void;
maxParallelTasks?: number;
}

// Empty state content for each column
Expand All @@ -54,6 +60,12 @@ const getEmptyStateContent = (status: TaskStatus, t: (key: string) => string): {
message: t('kanban.emptyBacklog'),
subtext: t('kanban.emptyBacklogHint')
};
case 'queue':
return {
icon: <Loader2 className="h-6 w-6 text-muted-foreground/50" />,
message: t('kanban.emptyQueue'),
subtext: t('kanban.emptyQueueHint')
};
case 'in_progress':
return {
icon: <Loader2 className="h-6 w-6 text-muted-foreground/50" />,
Expand Down Expand Up @@ -86,18 +98,23 @@ const getEmptyStateContent = (status: TaskStatus, t: (key: string) => string): {
}
};

function DroppableColumn({ status, tasks, onTaskClick, isOver, onAddClick, onArchiveAll }: DroppableColumnProps) {
function DroppableColumn({ status, tasks, onTaskClick, isOver, onAddClick, onArchiveAll, onQueueSettings, onQueueAll, maxParallelTasks }: DroppableColumnProps) {
const { t } = useTranslation('tasks');
const { setNodeRef } = useDroppable({
id: status
});

const taskIds = tasks.map((t) => t.id);

// Check if In Progress column is at capacity
const isInProgressFull = status === 'in_progress' && maxParallelTasks && tasks.length >= maxParallelTasks;

const getColumnBorderColor = (): string => {
switch (status) {
case 'backlog':
return 'column-backlog';
case 'queue':
return 'column-queue';
case 'in_progress':
return 'column-in-progress';
case 'ai_review':
Expand Down Expand Up @@ -129,19 +146,55 @@ function DroppableColumn({ status, tasks, onTaskClick, isOver, onAddClick, onArc
<h2 className="font-semibold text-sm text-foreground">
{TASK_STATUS_LABELS[status]}
</h2>
<span className="column-count-badge">
{tasks.length}
</span>
{status === 'in_progress' && maxParallelTasks ? (
<span className={cn(
"column-count-badge",
isInProgressFull && "bg-warning/20 text-warning border-warning/30"
)}>
{tasks.length}/{maxParallelTasks}
</span>
) : (
<span className="column-count-badge">
{tasks.length}
</span>
)}
</div>
<div className="flex items-center gap-1">
{status === 'backlog' && onAddClick && (
{status === 'backlog' && (
<>
{onQueueAll && tasks.length > 0 && (
<Button
variant="ghost"
size="icon"
className="h-7 w-7 hover:bg-cyan-500/10 hover:text-cyan-400 transition-colors"
onClick={onQueueAll}
title={t('tooltips.queueAll')}
>
<ListPlus className="h-4 w-4" />
</Button>
)}
{onAddClick && (
<Button
variant="ghost"
size="icon"
className="h-7 w-7 hover:bg-primary/10 hover:text-primary transition-colors"
onClick={onAddClick}
title={t('tooltips.addTask')}
>
<Plus className="h-4 w-4" />
</Button>
)}
</>
)}
{status === 'queue' && onQueueSettings && (
<Button
variant="ghost"
size="icon"
className="h-7 w-7 hover:bg-primary/10 hover:text-primary transition-colors"
onClick={onAddClick}
className="h-7 w-7 hover:bg-cyan-500/10 hover:text-cyan-400 transition-colors"
onClick={onQueueSettings}
title="Queue Settings"
>
<Plus className="h-4 w-4" />
<Settings className="h-4 w-4" />
</Button>
)}
Comment on lines +189 to 199
Copy link
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸก Minor

Localize the โ€œQueue Settingsโ€ tooltip text.

The title attribute for the queue settings button is hard-coded:

title="Queue Settings"

Per the frontend i18n guidelines, this should use a translation key via t(...) (e.g., t('tooltips.queueSettings')) and be backed by entries in the EN/FR locale files.

๐Ÿค– Prompt for AI Agents
In apps/frontend/src/renderer/components/KanbanBoard.tsx around lines 189 to
199, the button's title is hard-coded as "Queue Settings"; replace it with a
localized string using the i18n helper (e.g.,
title={t('tooltips.queueSettings')}). Ensure the component imports and calls the
translation hook (useTranslation or t from your i18n setup) and add matching
keys to the EN and FR locale files (tooltips.queueSettings -> "Queue Settings" /
"Paramรจtres de la file" or equivalent).

{status === 'done' && onArchiveAll && tasks.length > 0 && (
Expand Down Expand Up @@ -216,6 +269,13 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardP
const [activeTask, setActiveTask] = useState<Task | null>(null);
const [overColumnId, setOverColumnId] = useState<string | null>(null);
const [showArchived, setShowArchived] = useState(false);
const [showQueueSettings, setShowQueueSettings] = useState(false);

// Get active project for queue settings
const { getActiveProject } = useProjectStore();
const activeProject = getActiveProject();
const projectId = tasks.length > 0 ? tasks[0].projectId : activeProject?.id || '';
const maxParallelTasks = activeProject?.settings.maxParallelTasks ?? 3;

// Count archived tasks for display
const archivedCount = useMemo(() => {
Expand Down Expand Up @@ -244,6 +304,7 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardP
const tasksByStatus = useMemo(() => {
const grouped: Record<TaskStatus, Task[]> = {
backlog: [],
queue: [],
in_progress: [],
ai_review: [],
human_review: [],
Expand Down Expand Up @@ -285,6 +346,36 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardP
}
};

const handleSaveQueueSettings = async (newMaxParallel: number) => {
if (!projectId) {
console.error('[KanbanBoard] No projectId found');
return;
}

const success = await updateProjectSettings(projectId, {
maxParallelTasks: newMaxParallel
});

if (!success) {
console.error('[KanbanBoard] Failed to save queue settings');
}
};

const handleQueueAll = async () => {
const backlogTasks = tasksByStatus.backlog;
if (backlogTasks.length === 0) return;

console.log(`[Queue] Moving ${backlogTasks.length} tasks from Planning to Queue`);

// Move all backlog tasks to queue
for (const task of backlogTasks) {
await persistTaskStatus(task.id, 'queue');
}

// After all tasks are queued, process the queue to start filling In Progress
await processQueue();
};
Comment on lines +364 to +377
Copy link
Contributor

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸ  Major

๐Ÿงฉ Analysis chain

๐Ÿ Script executed:

# First, let's check if the file exists and understand its structure
ls -la apps/frontend/src/renderer/components/KanbanBoard.tsx

Repository: AndyMik90/Auto-Claude

Length of output: 168


๐Ÿ Script executed:

# Get line count to understand the file size
wc -l apps/frontend/src/renderer/components/KanbanBoard.tsx

Repository: AndyMik90/Auto-Claude

Length of output: 122


๐Ÿ Script executed:

# Now let's examine the specific lines mentioned in the review (364-377)
sed -n '364,377p' apps/frontend/src/renderer/components/KanbanBoard.tsx

Repository: AndyMik90/Auto-Claude

Length of output: 530


๐Ÿ Script executed:

# Also check the lines mentioned in "Also applies to: 441-500"
sed -n '441,500p' apps/frontend/src/renderer/components/KanbanBoard.tsx

Repository: AndyMik90/Auto-Claude

Length of output: 2601


๐Ÿ Script executed:

# Let's search for the processQueue function definition
rg "const processQueue" apps/frontend/src/renderer/components/KanbanBoard.tsx -A 30

Repository: AndyMik90/Auto-Claude

Length of output: 1246


๐Ÿ Script executed:

# Search for handleDragEnd function
rg "const handleDragEnd" apps/frontend/src/renderer/components/KanbanBoard.tsx -A 40

Repository: AndyMik90/Auto-Claude

Length of output: 1415


๐Ÿ Script executed:

# Look for hardcoded strings, especially "Queue Settings"
rg "Queue Settings" apps/frontend/src/renderer/components/KanbanBoard.tsx

Repository: AndyMik90/Auto-Claude

Length of output: 136


๐Ÿ Script executed:

# Check if projectId is used in the component
rg "projectId" apps/frontend/src/renderer/components/KanbanBoard.tsx -B 2 -A 2

Repository: AndyMik90/Auto-Claude

Length of output: 1281


๐Ÿ Script executed:

# Search for hardcoded strings in JSX (looking for common patterns like title, placeholder, label, aria-label, etc.)
rg '(title|placeholder|label|aria-label|aria-placeholder)=["'"'"']' apps/frontend/src/renderer/components/KanbanBoard.tsx

Repository: AndyMik90/Auto-Claude

Length of output: 101


๐Ÿ Script executed:

# Also check for hardcoded text in JSX elements (words directly in JSX without variables)
rg '<(button|div|span|p|h[1-6]|label).*>' apps/frontend/src/renderer/components/KanbanBoard.tsx -A 1 | grep -E '(Queue|Settings|Planning|Progress|Done|Backlog|Archive|Clear)' | head -20

Repository: AndyMik90/Auto-Claude

Length of output: 47


๐Ÿ Script executed:

# Look at the entire handleDragEnd to verify the exact projectId issue
sed -n '441,478p' apps/frontend/src/renderer/components/KanbanBoard.tsx

Repository: AndyMik90/Auto-Claude

Length of output: 1703


Scope queue capacity and auto-promotion per project, not globally.

Both the drag-end capacity check in handleDragEnd (lines 454-465) and processQueue count all in_progress / queue tasks in useTaskStore.getState().tasks without filtering by projectId, while maxParallelTasks is derived from the active project. If multiple projects are loaded, one project's tasks can incorrectly consume capacity and block promotions for another. Since projectId is available in the component, filter task counts by project when enforcing capacity limits.

Also, the hardcoded string title="Queue Settings" in the JSX violates the i18n guideline. Replace with a translation key using the useTranslation() hook (format: namespace:section.key).

Example refactor to make processQueue project-aware
-  const processQueue = async () => {
+  const processQueue = async () => {
+    if (!projectId) {
+      return;
+    }
     // Loop until capacity is full or queue is empty
     while (true) {
       // Get CURRENT state from store to ensure accuracy
       const currentTasks = useTaskStore.getState().tasks;
-      const inProgressCount = currentTasks.filter((t) =>
-        t.status === 'in_progress' && !t.metadata?.archivedAt
-      ).length;
-      const queuedTasks = currentTasks.filter((t) =>
-        t.status === 'queue' && !t.metadata?.archivedAt
-      );
+      const projectTasks = currentTasks.filter(
+        (t) => t.projectId === projectId && !t.metadata?.archivedAt
+      );
+      const inProgressCount = projectTasks.filter(
+        (t) => t.status === 'in_progress'
+      ).length;
+      const queuedTasks = projectTasks.filter(
+        (t) => t.status === 'queue'
+      );
@@
-      console.log(`[Queue] Auto-promoting task ${nextTask.id} from Queue to In Progress (${inProgressCount + 1}/${maxParallelTasks})`);
+      console.log(
+        `[Queue] Auto-promoting task ${nextTask.id} from Queue to In Progress (${inProgressCount + 1}/${maxParallelTasks})`
+      );
       await persistTaskStatus(nextTask.id, 'in_progress');
     }
   };

And in handleDragEnd, use the same project-scoped inProgressCount:

-      const currentTasks = useTaskStore.getState().tasks;
-      const inProgressCount = currentTasks.filter((t) =>
-        t.status === 'in_progress' && !t.metadata?.archivedAt
-      ).length;
+      const currentTasks = useTaskStore.getState().tasks;
+      const inProgressCount = currentTasks.filter(
+        (t) =>
+          t.projectId === projectId &&
+          t.status === 'in_progress' &&
+          !t.metadata?.archivedAt
+      ).length;

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


const handleDragStart = (event: DragStartEvent) => {
const { active } = event;
const task = tasks.find((t) => t.id === active.id);
Expand Down Expand Up @@ -316,7 +407,7 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardP
}
};

const handleDragEnd = (event: DragEndEvent) => {
const handleDragEnd = async (event: DragEndEvent) => {
const { active, over } = event;
setActiveTask(null);
setOverColumnId(null);
Expand All @@ -326,26 +417,86 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardP
const activeTaskId = active.id as string;
const overId = over.id as string;

// Determine target status
let newStatus: TaskStatus | null = null;

// Check if dropped on a column
if (TASK_STATUS_COLUMNS.includes(overId as TaskStatus)) {
const newStatus = overId as TaskStatus;
const task = tasks.find((t) => t.id === activeTaskId);
newStatus = overId as TaskStatus;
} else {
// Check if dropped on another task - move to that task's column
const overTask = tasks.find((t) => t.id === overId);
if (overTask) {
newStatus = overTask.status;
}
}

if (!newStatus) return;

const task = tasks.find((t) => t.id === activeTaskId);
if (!task || task.status === newStatus) return;

if (task && task.status !== newStatus) {
// Persist status change to file and update local state
persistTaskStatus(activeTaskId, newStatus);
const oldStatus = task.status;

// ============================================
// QUEUE SYSTEM: Enforce parallel task limit
// ============================================
if (newStatus === 'in_progress') {
// Get CURRENT state from store directly to avoid stale prop/memo issues during rapid dragging
const currentTasks = useTaskStore.getState().tasks;
const inProgressCount = currentTasks.filter((t) =>
t.status === 'in_progress' && !t.metadata?.archivedAt
).length;

// If limit reached, move to queue instead (unless already coming from queue)
if (inProgressCount >= maxParallelTasks && oldStatus !== 'queue') {
console.log(`[Queue] In Progress full (${inProgressCount}/${maxParallelTasks}), moving task to Queue`);
newStatus = 'queue';
}
return;
}

// Check if dropped on another task - move to that task's column
const overTask = tasks.find((t) => t.id === overId);
if (overTask) {
const task = tasks.find((t) => t.id === activeTaskId);
if (task && task.status !== overTask.status) {
// Persist status change to file and update local state
persistTaskStatus(activeTaskId, overTask.status);
// Persist status change to file and update local state
await persistTaskStatus(activeTaskId, newStatus);

// ============================================
// QUEUE SYSTEM: Auto-process queue when slot opens
// ============================================
if (oldStatus === 'in_progress' && newStatus !== 'in_progress') {
// A task left In Progress - check if we can promote from queue
await processQueue();
}
};

/**
* Automatically move tasks from Queue to In Progress to fill available capacity
* Promotes multiple tasks if needed (e.g., after bulk queue)
*/
const processQueue = async () => {
// Loop until capacity is full or queue is empty
while (true) {
// Get CURRENT state from store to ensure accuracy
const currentTasks = useTaskStore.getState().tasks;
const inProgressCount = currentTasks.filter((t) =>
t.status === 'in_progress' && !t.metadata?.archivedAt
).length;
const queuedTasks = currentTasks.filter((t) =>
t.status === 'queue' && !t.metadata?.archivedAt
);

// Stop if no capacity or no queued tasks
if (inProgressCount >= maxParallelTasks || queuedTasks.length === 0) {
break;
}

// Get the oldest task in queue (FIFO ordering)
const nextTask = queuedTasks.sort((a, b) => {
const dateA = new Date(a.createdAt).getTime();
const dateB = new Date(b.createdAt).getTime();
return dateA - dateB; // Ascending order (oldest first)
})[0];

console.log(`[Queue] Auto-promoting task ${nextTask.id} from Queue to In Progress (${inProgressCount + 1}/${maxParallelTasks})`);
await persistTaskStatus(nextTask.id, 'in_progress');
}
};

Expand Down Expand Up @@ -391,7 +542,10 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardP
onTaskClick={onTaskClick}
isOver={overColumnId === status}
onAddClick={status === 'backlog' ? onNewTaskClick : undefined}
onQueueAll={status === 'backlog' ? handleQueueAll : undefined}
onQueueSettings={status === 'queue' ? () => setShowQueueSettings(true) : undefined}
onArchiveAll={status === 'done' ? handleArchiveAll : undefined}
maxParallelTasks={status === 'in_progress' ? maxParallelTasks : undefined}
/>
))}
</div>
Expand All @@ -405,6 +559,15 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick }: KanbanBoardP
) : null}
</DragOverlay>
</DndContext>

{/* Queue Settings Modal */}
<QueueSettingsModal
open={showQueueSettings}
onOpenChange={setShowQueueSettings}
projectId={projectId}
currentMaxParallel={maxParallelTasks}
onSave={handleSaveQueueSettings}
/>
</div>
);
}
Loading
Loading