feat: implement pipeline feature for automated workflow steps

- Introduced a new pipeline service to manage custom workflow steps that execute after a feature is marked "In Progress".
- Added API endpoints for configuring, saving, adding, updating, deleting, and reordering pipeline steps.
- Enhanced the UI to support pipeline settings, including a dialog for managing steps and integration with the Kanban board.
- Updated the application state management to handle pipeline configurations per project.
- Implemented dynamic column generation in the Kanban board to display pipeline steps between "In Progress" and "Waiting Approval".
- Added documentation for the new pipeline feature, including usage instructions and configuration details.

This feature allows for a more structured workflow, enabling automated processes such as code reviews and testing after feature implementation.
This commit is contained in:
Test User
2025-12-27 23:57:15 -05:00
parent 4a708aa305
commit e9b366fa18
28 changed files with 2409 additions and 61 deletions

View File

@@ -23,7 +23,8 @@ export function useBoardColumnFeatures({
}: UseBoardColumnFeaturesProps) {
// Memoize column features to prevent unnecessary re-renders
const columnFeaturesMap = useMemo(() => {
const map: Record<ColumnId, Feature[]> = {
// Use a more flexible type to support dynamic pipeline statuses
const map: Record<string, Feature[]> = {
backlog: [],
in_progress: [],
waiting_approval: [],
@@ -76,31 +77,56 @@ export function useBoardColumnFeatures({
matchesWorktree = featureBranch === effectiveBranch;
}
// Use the feature's status (fallback to backlog for unknown statuses)
const status = f.status || 'backlog';
// IMPORTANT:
// Historically, we forced "running" features into in_progress so they never disappeared
// during stale reload windows. With pipelines, a feature can legitimately be running while
// its status is `pipeline_*`, so we must respect that status to render it in the right column.
if (isRunning) {
// Only show running tasks if they match the current worktree
if (matchesWorktree) {
if (!matchesWorktree) return;
if (status.startsWith('pipeline_')) {
if (!map[status]) map[status] = [];
map[status].push(f);
return;
}
// If it's running and has a known non-backlog status, keep it in that status.
// Otherwise, fallback to in_progress as the "active work" column.
if (status !== 'backlog' && map[status]) {
map[status].push(f);
} else {
map.in_progress.push(f);
}
} else {
// Otherwise, use the feature's status (fallback to backlog for unknown statuses)
const status = f.status as ColumnId;
return;
}
// Filter all items by worktree, including backlog
// This ensures backlog items with a branch assigned only show in that branch
if (status === 'backlog') {
if (matchesWorktree) {
map.backlog.push(f);
}
} else if (map[status]) {
// Only show if matches current worktree or has no worktree assigned
if (matchesWorktree) {
map[status].push(f);
}
} else {
// Unknown status, default to backlog
if (matchesWorktree) {
map.backlog.push(f);
// Not running: place by status (and worktree filter)
// Filter all items by worktree, including backlog
// This ensures backlog items with a branch assigned only show in that branch
if (status === 'backlog') {
if (matchesWorktree) {
map.backlog.push(f);
}
} else if (map[status]) {
// Only show if matches current worktree or has no worktree assigned
if (matchesWorktree) {
map[status].push(f);
}
} else if (status.startsWith('pipeline_')) {
// Handle pipeline statuses - initialize array if needed
if (matchesWorktree) {
if (!map[status]) {
map[status] = [];
}
map[status].push(f);
}
} else {
// Unknown status, default to backlog
if (matchesWorktree) {
map.backlog.push(f);
}
}
});
@@ -147,7 +173,7 @@ export function useBoardColumnFeatures({
const getColumnFeatures = useCallback(
(columnId: ColumnId) => {
return columnFeaturesMap[columnId];
return columnFeaturesMap[columnId] || [];
},
[columnFeaturesMap]
);

View File

@@ -203,6 +203,11 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
// This ensures the feature card shows the "Approve Plan" button
console.log('[Board] Plan approval required, reloading features...');
loadFeatures();
} else if (event.type === 'pipeline_step_started') {
// Pipeline steps update the feature status to `pipeline_*` before the step runs.
// Reload so the card moves into the correct pipeline column immediately.
console.log('[Board] Pipeline step started, reloading features...');
loadFeatures();
} else if (event.type === 'auto_mode_error') {
// Reload features when an error occurs (feature moved to waiting_approval)
console.log('[Board] Feature error, reloading features...', event.error);