From 945340f6090acb47380e6e3a7178d51cff52e776 Mon Sep 17 00:00:00 2001 From: MacJediWizard <99237059+MacJediWizard@users.noreply.github.com> Date: Fri, 1 May 2026 15:02:50 -0400 Subject: [PATCH] fix(kanban): validator falls back to global columns when project has no specifics PUT /api/tasks//status returns 400 "Invalid status" whenever the task belongs to a project that has no project-specific kanban_columns rows AND the user drops it into a configured global column other than the four hardcoded fallback keys. Reproduction: 1. Project has no project-specific kanban_columns rows. 2. The instance has 5 globals (project_id IS NULL): todo, in_progress, review, done, on_hold. 3. The kanban UI renders the 5 globals as drop targets for that project's tasks. 4. User drops a task into "On Hold". Frontend sends status="on_hold". 5. app/routes/tasks.py:1519 calls KanbanColumn.get_valid_status_keys(project_id=task.project_id) with the project's id. 6. get_active_columns(project_id=) filters strictly on project_id and returns []. 7. get_valid_status_keys then falls back to the hardcoded list ["todo", "in_progress", "review", "done", "cancelled"] which is missing "on_hold" (and includes "cancelled", which isn't even a configured column). 8. "on_hold" is not in that list -> 400. Drops to the four hardcoded keys all returned 200; only "On Hold" failed, exactly matching the live 200/400 alternation observed in production logs. Fix: when there are no project-specific columns, fall back to the configured global columns from the database (which is the set the UI is already rendering). The hardcoded list is only used as a last-ditch fallback when even the globals table is empty - this preserves the table-not-yet-seeded safety net during fresh migrations. Pure validator change; no schema change, no behavioural change beyond accepting the statuses the UI is already offering. --- app/models/kanban_column.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/models/kanban_column.py b/app/models/kanban_column.py index 9b1d3bec..08bfcdd5 100644 --- a/app/models/kanban_column.py +++ b/app/models/kanban_column.py @@ -108,10 +108,22 @@ class KanbanColumn(db.Model): @classmethod def get_valid_status_keys(cls, project_id=None): - """Get list of all valid status keys (for validation). If project_id is None, returns global column keys.""" + """Get list of all valid status keys (for validation). + + If project_id is None, returns global column keys. + + If project_id is set but the project has no project-specific + columns, fall back to the configured global columns. The kanban + UI renders global columns in that case, so the validator must + accept the same set — otherwise drops to globally-defined columns + like "on_hold" come back as 400 "Invalid status". + """ columns = cls.get_active_columns(project_id=project_id) + if not columns and project_id is not None: + columns = cls.get_active_columns(project_id=None) if not columns: - # Fallback to default statuses if table doesn't exist + # Last-ditch fallback if even global columns are missing + # (e.g. table not yet seeded during a fresh migration). return ["todo", "in_progress", "review", "done", "cancelled"] return [col.key for col in columns]