feat: add display name field for tasks (#20856)

## Problem

Tasks currently only expose a machine-friendly name field (e.g.
`task-python-debug-a1b2`), but this value is primarily an identifier
rather than a clean, descriptive label. We need a separate
display-friendly name for use in the UI.

This PR introduces a new `display_name` field and updates the task-name
generation flow. The Claude system prompt was updated to return valid
JSON with both `name` and `display_name`. The name generation logic
follows a fallback chain (Anthropic > prompt sanitization > random
fallback). To make task names more closely resemble their display names,
the legacy `task-` prefix has been removed. For context, PR
https://github.com/coder/coder/pull/20834 introduced a small Task icon
to the workspace list to help identify workspaces associated to tasks.

## Changes

- Database migration: Added `display_name` column to tasks table
- Updated system prompt to generate both task name and display name as
valid JSON
- Task name generation now follows a fallback chain: Anthropic > prompt
sanitization > random fallback
- Removed `task-` prefix from task names to allow more descriptive names
- Note: PR https://github.com/coder/coder/pull/20834 adds a Task icon to
workspaces in the workspace list to distinguish task-created workspaces

**Note:** UI changes will be addressed in a follow-up PR

Related to: https://github.com/coder/coder/issues/20801
This commit is contained in:
Susana Ferreira
2025-11-25 13:00:59 +00:00
committed by GitHub
parent e8bf074022
commit 3011207519
23 changed files with 780 additions and 124 deletions
+20 -10
View File
@@ -13187,7 +13187,7 @@ SET
WHERE
id = $2::uuid
AND deleted_at IS NULL
RETURNING id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at
RETURNING id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name
`
type DeleteTaskParams struct {
@@ -13209,12 +13209,13 @@ func (q *sqlQuerier) DeleteTask(ctx context.Context, arg DeleteTaskParams) (Task
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
)
return i, err
}
const getTaskByID = `-- name: GetTaskByID :one
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE id = $1::uuid
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE id = $1::uuid
`
func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error) {
@@ -13231,6 +13232,7 @@ func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
&i.Status,
&i.StatusDebug,
&i.WorkspaceBuildNumber,
@@ -13246,7 +13248,7 @@ func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error
}
const getTaskByOwnerIDAndName = `-- name: GetTaskByOwnerIDAndName :one
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status
WHERE
owner_id = $1::uuid
AND deleted_at IS NULL
@@ -13272,6 +13274,7 @@ func (q *sqlQuerier) GetTaskByOwnerIDAndName(ctx context.Context, arg GetTaskByO
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
&i.Status,
&i.StatusDebug,
&i.WorkspaceBuildNumber,
@@ -13287,7 +13290,7 @@ func (q *sqlQuerier) GetTaskByOwnerIDAndName(ctx context.Context, arg GetTaskByO
}
const getTaskByWorkspaceID = `-- name: GetTaskByWorkspaceID :one
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE workspace_id = $1::uuid
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE workspace_id = $1::uuid
`
func (q *sqlQuerier) GetTaskByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (Task, error) {
@@ -13304,6 +13307,7 @@ func (q *sqlQuerier) GetTaskByWorkspaceID(ctx context.Context, workspaceID uuid.
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
&i.Status,
&i.StatusDebug,
&i.WorkspaceBuildNumber,
@@ -13320,10 +13324,10 @@ func (q *sqlQuerier) GetTaskByWorkspaceID(ctx context.Context, workspaceID uuid.
const insertTask = `-- name: InsertTask :one
INSERT INTO tasks
(id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at)
(id, organization_id, owner_id, name, display_name, workspace_id, template_version_id, template_parameters, prompt, created_at)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name
`
type InsertTaskParams struct {
@@ -13331,6 +13335,7 @@ type InsertTaskParams struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
Name string `db:"name" json:"name"`
DisplayName string `db:"display_name" json:"display_name"`
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
TemplateParameters json.RawMessage `db:"template_parameters" json:"template_parameters"`
@@ -13344,6 +13349,7 @@ func (q *sqlQuerier) InsertTask(ctx context.Context, arg InsertTaskParams) (Task
arg.OrganizationID,
arg.OwnerID,
arg.Name,
arg.DisplayName,
arg.WorkspaceID,
arg.TemplateVersionID,
arg.TemplateParameters,
@@ -13362,12 +13368,13 @@ func (q *sqlQuerier) InsertTask(ctx context.Context, arg InsertTaskParams) (Task
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
)
return i, err
}
const listTasks = `-- name: ListTasks :many
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status tws
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status tws
WHERE tws.deleted_at IS NULL
AND CASE WHEN $1::UUID != '00000000-0000-0000-0000-000000000000' THEN tws.owner_id = $1::UUID ELSE TRUE END
AND CASE WHEN $2::UUID != '00000000-0000-0000-0000-000000000000' THEN tws.organization_id = $2::UUID ELSE TRUE END
@@ -13401,6 +13408,7 @@ func (q *sqlQuerier) ListTasks(ctx context.Context, arg ListTasksParams) ([]Task
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
&i.Status,
&i.StatusDebug,
&i.WorkspaceBuildNumber,
@@ -13433,7 +13441,7 @@ SET
WHERE
id = $2::uuid
AND deleted_at IS NULL
RETURNING id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at
RETURNING id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, display_name
`
type UpdateTaskPromptParams struct {
@@ -13455,6 +13463,7 @@ func (q *sqlQuerier) UpdateTaskPrompt(ctx context.Context, arg UpdateTaskPromptP
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
)
return i, err
}
@@ -13476,7 +13485,7 @@ WHERE
AND w.id = $2
AND tv.id = tasks.template_version_id
RETURNING
tasks.id, tasks.organization_id, tasks.owner_id, tasks.name, tasks.workspace_id, tasks.template_version_id, tasks.template_parameters, tasks.prompt, tasks.created_at, tasks.deleted_at
tasks.id, tasks.organization_id, tasks.owner_id, tasks.name, tasks.workspace_id, tasks.template_version_id, tasks.template_parameters, tasks.prompt, tasks.created_at, tasks.deleted_at, tasks.display_name
`
type UpdateTaskWorkspaceIDParams struct {
@@ -13498,6 +13507,7 @@ func (q *sqlQuerier) UpdateTaskWorkspaceID(ctx context.Context, arg UpdateTaskWo
&i.Prompt,
&i.CreatedAt,
&i.DeletedAt,
&i.DisplayName,
)
return i, err
}