-- `cards.position` was a project-wide sort key. It is now a rank *within a -- column* — the group of cards sharing a (project_id, status_id). The inbox -- (status_id IS NULL) is its own column. The "all tasks" view no longer uses -- position at all (it sorts alphabetically); ordering is a per-column concern -- driven by PUT /api/projects/{id}/cards/order. DROP INDEX IF EXISTS idx_cards_project_position; CREATE INDEX IF NOT EXISTS idx_cards_project_status_position ON cards (project_id, status_id, position); -- Re-pack every existing column to a dense 0..n-1, preserving current order. -- The CTE is evaluated against the pre-update snapshot, so this is safe despite -- writing the same table. WITH ranked (id, rk) AS ( SELECT id, row_number() OVER ( PARTITION BY project_id, status_id ORDER BY position, id ) - 1 FROM cards ) UPDATE cards SET position = (SELECT rk FROM ranked WHERE ranked.id = cards.id);