= 1 ? "bg-txt-secondary" : "bg-txt-quaternary",
+ "h-1 w-[3px] rounded",
+ bars >= 1 ? "bg-txt-secondary" : "bg-txt-quaternary",
)}
/>
= 2 ? "bg-txt-secondary" : "bg-txt-quaternary",
+ "h-2 w-[3px] rounded",
+ bars >= 2 ? "bg-txt-secondary" : "bg-txt-quaternary",
)}
/>
= 3 ? "bg-txt-secondary" : "bg-txt-quaternary",
+ "h-3 w-[3px] rounded",
+ bars >= 3 ? "bg-txt-secondary" : "bg-txt-quaternary",
)}
/>
diff --git a/pkg/coredata/migrations/20260330T120000Z.sql b/pkg/coredata/migrations/20260330T120000Z.sql
new file mode 100644
index 000000000..7a53ba206
--- /dev/null
+++ b/pkg/coredata/migrations/20260330T120000Z.sql
@@ -0,0 +1,36 @@
+-- Rename priority to rank
+ALTER TABLE tasks RENAME COLUMN priority TO rank;
+
+ALTER TABLE tasks DROP CONSTRAINT tasks_organization_id_state_priority_key;
+
+-- Add task priority enum
+CREATE TYPE task_priority AS ENUM ('URGENT', 'HIGH', 'MEDIUM', 'LOW');
+
+ALTER TABLE tasks ADD COLUMN priority task_priority NOT NULL DEFAULT 'MEDIUM'::task_priority;
+
+ALTER TABLE tasks ALTER COLUMN priority DROP DEFAULT;
+
+-- Rank is now scoped to (state, priority) — backfill ranks per group
+WITH ranked AS (
+ SELECT id, ROW_NUMBER() OVER (
+ PARTITION BY organization_id, state, priority
+ ORDER BY rank
+ ) AS new_rank
+ FROM tasks
+)
+UPDATE tasks SET rank = ranked.new_rank FROM ranked WHERE tasks.id = ranked.id;
+
+ALTER TABLE tasks
+ADD CONSTRAINT tasks_organization_id_state_priority_rank_key
+ UNIQUE (organization_id, state, priority, rank)
+ DEFERRABLE INITIALLY DEFERRED;
+
+-- Computed column for composite ordering (priority level then rank)
+ALTER TABLE tasks ADD COLUMN priority_rank int GENERATED ALWAYS AS (
+ (CASE priority
+ WHEN 'URGENT' THEN 1
+ WHEN 'HIGH' THEN 2
+ WHEN 'MEDIUM' THEN 3
+ WHEN 'LOW' THEN 4
+ END) * 1000000 + rank
+) STORED;
diff --git a/pkg/coredata/task.go b/pkg/coredata/task.go
index 93606770f..e43547a30 100644
--- a/pkg/coredata/task.go
+++ b/pkg/coredata/task.go
@@ -37,13 +37,17 @@ type (
Name string `db:"name"`
Description *string `db:"description"`
State TaskState `db:"state"`
+ Priority TaskPriority `db:"priority"`
ReferenceID string `db:"reference_id"`
TimeEstimate *time.Duration `db:"time_estimate"`
AssignedToID *gid.GID `db:"assigned_to_profile_id"`
Deadline *time.Time `db:"deadline"`
- Priority int `db:"priority"`
+ Rank int `db:"rank"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
+
+ // ordering only
+ PriorityRank int `db:"priority_rank"`
}
Tasks []*Task
@@ -51,8 +55,8 @@ type (
func (t Task) CursorKey(orderBy TaskOrderField) page.CursorKey {
switch orderBy {
- case TaskOrderFieldPriority:
- return page.NewCursorKey(t.ID, t.Priority)
+ case TaskOrderFieldPriorityRank:
+ return page.NewCursorKey(t.ID, t.PriorityRank)
case TaskOrderFieldCreatedAt:
return page.NewCursorKey(t.ID, t.CreatedAt)
}
@@ -88,11 +92,13 @@ SELECT
name,
description,
state,
+ priority,
reference_id,
time_estimate,
assigned_to_profile_id,
deadline,
- priority,
+ rank,
+ priority_rank,
created_at,
updated_at
FROM
@@ -141,11 +147,13 @@ SELECT
name,
description,
state,
+ priority,
reference_id,
time_estimate,
assigned_to_profile_id,
deadline,
- priority,
+ rank,
+ priority_rank,
created_at,
updated_at
FROM
@@ -181,10 +189,10 @@ func (t *Task) Insert(
scope Scoper,
) error {
q := `
-WITH next_priority AS (
- SELECT COALESCE(MAX(priority), 0) + 1 AS value
+WITH next_rank AS (
+ SELECT COALESCE(MAX(rank), 0) + 1 AS value
FROM tasks
- WHERE organization_id = @organization_id AND state = @state
+ WHERE organization_id = @organization_id AND state = @state AND priority = @priority
)
INSERT INTO
tasks (
@@ -196,10 +204,11 @@ INSERT INTO
description,
reference_id,
state,
+ priority,
time_estimate,
assigned_to_profile_id,
deadline,
- priority,
+ rank,
created_at,
updated_at
)
@@ -212,14 +221,15 @@ VALUES (
@description,
@reference_id,
@state,
+ @priority,
@time_estimate,
@assigned_to_profile_id,
@deadline,
- (SELECT value FROM next_priority),
+ (SELECT value FROM next_rank),
@created_at,
@updated_at
)
-RETURNING priority;
+RETURNING rank, priority_rank;
`
args := pgx.StrictNamedArgs{
@@ -231,6 +241,7 @@ RETURNING priority;
"description": t.Description,
"reference_id": t.ReferenceID,
"state": t.State,
+ "priority": t.Priority,
"time_estimate": t.TimeEstimate,
"assigned_to_profile_id": t.AssignedToID,
"deadline": t.Deadline,
@@ -238,7 +249,7 @@ RETURNING priority;
"updated_at": t.UpdatedAt,
}
- err := conn.QueryRow(ctx, q, args).Scan(&t.Priority)
+ err := conn.QueryRow(ctx, q, args).Scan(&t.Rank, &t.PriorityRank)
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
@@ -258,10 +269,10 @@ func (t *Task) Upsert(
scope Scoper,
) error {
q := `
-WITH next_priority AS (
- SELECT COALESCE(MAX(priority), 0) + 1 AS value
+WITH next_rank AS (
+ SELECT COALESCE(MAX(rank), 0) + 1 AS value
FROM tasks
- WHERE organization_id = @organization_id AND state = @state
+ WHERE organization_id = @organization_id AND state = @state AND priority = @priority
)
INSERT INTO
tasks (
@@ -273,10 +284,11 @@ INSERT INTO
description,
reference_id,
state,
+ priority,
time_estimate,
assigned_to_profile_id,
deadline,
- priority,
+ rank,
created_at,
updated_at
)
@@ -289,10 +301,11 @@ VALUES (
@description,
@reference_id,
@state,
+ @priority,
@time_estimate,
@assigned_to_profile_id,
@deadline,
- (SELECT value FROM next_priority),
+ (SELECT value FROM next_rank),
@created_at,
@updated_at
)
@@ -309,10 +322,12 @@ RETURNING
description,
reference_id,
state,
+ priority,
time_estimate,
assigned_to_profile_id,
deadline,
- priority,
+ rank,
+ priority_rank,
created_at,
updated_at
`
@@ -326,6 +341,7 @@ RETURNING
"description": t.Description,
"reference_id": t.ReferenceID,
"state": t.State,
+ "priority": t.Priority,
"time_estimate": t.TimeEstimate,
"assigned_to_profile_id": t.AssignedToID,
"deadline": t.Deadline,
@@ -394,11 +410,13 @@ func (t *Tasks) LoadByOrganizationID(
name,
description,
state,
+ priority,
reference_id,
time_estimate,
assigned_to_profile_id,
deadline,
- priority,
+ rank,
+ priority_rank,
created_at,
updated_at
FROM
@@ -476,11 +494,13 @@ SELECT
name,
description,
state,
+ priority,
reference_id,
time_estimate,
assigned_to_profile_id,
deadline,
- priority,
+ rank,
+ priority_rank,
created_at,
updated_at
FROM
@@ -523,6 +543,7 @@ SET
description = @description,
state = @state,
priority = @priority,
+ rank = @rank,
time_estimate = @time_estimate,
updated_at = @updated_at,
assigned_to_profile_id = @assigned_to_profile_id,
@@ -538,6 +559,7 @@ WHERE %s
"description": t.Description,
"state": t.State,
"priority": t.Priority,
+ "rank": t.Rank,
"time_estimate": t.TimeEstimate,
"updated_at": t.UpdatedAt,
"assigned_to_profile_id": t.AssignedToID,
@@ -550,17 +572,18 @@ WHERE %s
return err
}
-func (t *Task) NextPriorityForState(
+func (t *Task) NextRankForStatePriority(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
-SELECT COALESCE(MAX(priority), 0) + 1
+SELECT COALESCE(MAX(rank), 0) + 1
FROM tasks
WHERE
organization_id = @organization_id
AND state = @state
+ AND priority = @priority
AND id != @id
AND %s;
`
@@ -570,24 +593,25 @@ WHERE
"id": t.ID,
"organization_id": t.OrganizationID,
"state": t.State,
+ "priority": t.Priority,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
- return fmt.Errorf("cannot get next priority: %w", err)
+ return fmt.Errorf("cannot get next rank: %w", err)
}
- priority, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[int])
+ rank, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[int])
if err != nil {
- return fmt.Errorf("cannot get next priority: %w", err)
+ return fmt.Errorf("cannot get next rank: %w", err)
}
- t.Priority = priority
+ t.Rank = rank
return nil
}
-func (t *Task) UpdatePriority(
+func (t *Task) UpdateRank(
ctx context.Context,
conn pg.Conn,
scope Scoper,
@@ -595,18 +619,18 @@ func (t *Task) UpdatePriority(
q := `
WITH old AS (
SELECT
- priority AS old_priority
+ rank AS old_rank
FROM tasks
- WHERE %s AND id = @id AND organization_id = @organization_id AND state = @state
+ WHERE %s AND id = @id AND organization_id = @organization_id AND state = @state AND priority = @priority
)
UPDATE tasks
SET
- priority = CASE
- WHEN id = @id THEN @new_priority
- ELSE priority + CASE
- WHEN @new_priority < old.old_priority THEN 1
- WHEN @new_priority > old.old_priority THEN -1
+ rank = CASE
+ WHEN id = @id THEN @new_rank
+ ELSE rank + CASE
+ WHEN @new_rank < old.old_rank THEN 1
+ WHEN @new_rank > old.old_rank THEN -1
END
END,
updated_at = @updated_at
@@ -614,9 +638,10 @@ FROM old
WHERE %s
AND organization_id = @organization_id
AND state = @state
+ AND priority = @priority
AND (
id = @id
- OR (priority BETWEEN LEAST(old.old_priority, @new_priority) AND GREATEST(old.old_priority, @new_priority))
+ OR (rank BETWEEN LEAST(old.old_rank, @new_rank) AND GREATEST(old.old_rank, @new_rank))
);
`
@@ -625,16 +650,17 @@ WHERE %s
args := pgx.StrictNamedArgs{
"id": t.ID,
- "new_priority": t.Priority,
+ "new_rank": t.Rank,
"organization_id": t.OrganizationID,
"state": t.State,
+ "priority": t.Priority,
"updated_at": t.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
- return fmt.Errorf("cannot update task priority: %w", err)
+ return fmt.Errorf("cannot update task rank: %w", err)
}
return nil
diff --git a/pkg/coredata/task_order_field.go b/pkg/coredata/task_order_field.go
index cd4ce49e9..c82e7816f 100644
--- a/pkg/coredata/task_order_field.go
+++ b/pkg/coredata/task_order_field.go
@@ -21,14 +21,14 @@ type (
)
const (
- TaskOrderFieldPriority TaskOrderField = "PRIORITY"
- TaskOrderFieldCreatedAt TaskOrderField = "CREATED_AT"
+ TaskOrderFieldPriorityRank TaskOrderField = "PRIORITY_RANK" // ordering only
+ TaskOrderFieldCreatedAt TaskOrderField = "CREATED_AT"
)
func (p TaskOrderField) Column() string {
switch p {
- case TaskOrderFieldPriority:
- return "priority"
+ case TaskOrderFieldPriorityRank:
+ return "priority_rank"
case TaskOrderFieldCreatedAt:
return "created_at"
}
@@ -37,7 +37,7 @@ func (p TaskOrderField) Column() string {
func (p TaskOrderField) IsValid() bool {
switch p {
- case TaskOrderFieldPriority, TaskOrderFieldCreatedAt:
+ case TaskOrderFieldPriorityRank, TaskOrderFieldCreatedAt:
return true
}
return false
diff --git a/pkg/coredata/task_priority.go b/pkg/coredata/task_priority.go
new file mode 100644
index 000000000..ae0ff11e7
--- /dev/null
+++ b/pkg/coredata/task_priority.go
@@ -0,0 +1,72 @@
+// Copyright (c) 2026 Probo Inc
.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+package coredata
+
+import (
+ "database/sql/driver"
+ "fmt"
+)
+
+type TaskPriority string
+
+const (
+ TaskPriorityUrgent TaskPriority = "URGENT"
+ TaskPriorityHigh TaskPriority = "HIGH"
+ TaskPriorityMedium TaskPriority = "MEDIUM"
+ TaskPriorityLow TaskPriority = "LOW"
+)
+
+func TaskPriorities() []TaskPriority {
+ return []TaskPriority{
+ TaskPriorityUrgent,
+ TaskPriorityHigh,
+ TaskPriorityMedium,
+ TaskPriorityLow,
+ }
+}
+
+func (tp TaskPriority) String() string {
+ return string(tp)
+}
+
+func (tp *TaskPriority) Scan(value any) error {
+ var s string
+ switch v := value.(type) {
+ case string:
+ s = v
+ case []byte:
+ s = string(v)
+ default:
+ return fmt.Errorf("unsupported type for TaskPriority: %T", value)
+ }
+
+ switch s {
+ case "URGENT":
+ *tp = TaskPriorityUrgent
+ case "HIGH":
+ *tp = TaskPriorityHigh
+ case "MEDIUM":
+ *tp = TaskPriorityMedium
+ case "LOW":
+ *tp = TaskPriorityLow
+ default:
+ return fmt.Errorf("invalid TaskPriority value: %q", s)
+ }
+ return nil
+}
+
+func (tp TaskPriority) Value() (driver.Value, error) {
+ return tp.String(), nil
+}
diff --git a/pkg/probo/measure_service.go b/pkg/probo/measure_service.go
index f7968dfef..2b4467635 100644
--- a/pkg/probo/measure_service.go
+++ b/pkg/probo/measure_service.go
@@ -411,6 +411,7 @@ func (s MeasureService) Import(
Description: &taskDescription,
ReferenceID: req.Measures[i].Tasks[j].ReferenceID,
State: coredata.TaskStateTodo,
+ Priority: coredata.TaskPriorityMedium,
CreatedAt: now,
UpdatedAt: now,
}
diff --git a/pkg/probo/task_service.go b/pkg/probo/task_service.go
index 283a1deef..8b600e04f 100644
--- a/pkg/probo/task_service.go
+++ b/pkg/probo/task_service.go
@@ -37,6 +37,7 @@ type (
MeasureID *gid.GID
Name string
Description *string
+ Priority coredata.TaskPriority
TimeEstimate *time.Duration
AssignedToID *gid.GID
Deadline *time.Time
@@ -47,11 +48,12 @@ type (
Name *string
Description **string
State *coredata.TaskState
+ Priority *coredata.TaskPriority
TimeEstimate **time.Duration
Deadline **time.Time
AssignedToID **gid.GID
MeasureID **gid.GID
- Priority *int
+ Rank *int
}
)
@@ -62,6 +64,7 @@ func (ctr *CreateTaskRequest) Validate() error {
v.Check(ctr.MeasureID, "measure_id", validator.GID(coredata.MeasureEntityType))
v.Check(ctr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(ctr.Description, "description", validator.SafeText(ContentMaxLength))
+ v.Check(ctr.Priority, "priority", validator.Required(), validator.OneOfSlice(coredata.TaskPriorities()))
v.Check(ctr.TimeEstimate, "time_estimate", validator.RangeDuration(0, 1000*time.Hour))
v.Check(ctr.AssignedToID, "assigned_to_id", validator.GID(coredata.MembershipProfileEntityType))
@@ -74,10 +77,12 @@ func (utr *UpdateTaskRequest) Validate() error {
v.Check(utr.TaskID, "task_id", validator.Required(), validator.GID(coredata.TaskEntityType))
v.Check(utr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(utr.Description, "description", validator.SafeText(ContentMaxLength))
+ v.Check(utr.Priority, "priority", validator.OneOfSlice(coredata.TaskPriorities()))
v.Check(utr.TimeEstimate, "time_estimate", validator.RangeDuration(0, 1000*time.Hour))
v.Check(utr.State, "state", validator.OneOfSlice(coredata.TaskStates()))
v.Check(utr.AssignedToID, "assigned_to_id", validator.GID(coredata.MembershipProfileEntityType))
v.Check(utr.MeasureID, "measure_id", validator.GID(coredata.MeasureEntityType))
+ v.Check(utr.Rank, "rank", validator.Min(1))
return v.Error()
}
@@ -104,6 +109,7 @@ func (s TaskService) Create(
MeasureID: req.MeasureID,
Name: req.Name,
Description: req.Description,
+ Priority: req.Priority,
TimeEstimate: req.TimeEstimate,
AssignedToID: req.AssignedToID,
Deadline: req.Deadline,
@@ -275,6 +281,7 @@ func (s TaskService) Update(
}
oldState := task.State
+ oldPriority := task.Priority
if req.Name != nil {
task.Name = *req.Name
@@ -320,16 +327,19 @@ func (s TaskService) Update(
}
}
- task.UpdatedAt = time.Now()
-
if req.Priority != nil {
task.Priority = *req.Priority
- if err := task.UpdatePriority(ctx, conn, s.svc.scope); err != nil {
- return fmt.Errorf("cannot update task priority: %w", err)
- }
- } else if task.State != oldState {
- if err := task.NextPriorityForState(ctx, conn, s.svc.scope); err != nil {
- return fmt.Errorf("cannot get next priority: %w", err)
+ }
+
+ task.UpdatedAt = time.Now()
+
+ targetRank := req.Rank
+ priorityChanged := task.Priority != oldPriority
+ stateChanged := task.State != oldState
+
+ if priorityChanged || stateChanged {
+ if err := task.NextRankForStatePriority(ctx, conn, s.svc.scope); err != nil {
+ return fmt.Errorf("cannot get next rank: %w", err)
}
}
@@ -337,6 +347,13 @@ func (s TaskService) Update(
return fmt.Errorf("cannot update task: %w", err)
}
+ if targetRank != nil {
+ task.Rank = *targetRank
+ if err := task.UpdateRank(ctx, conn, s.svc.scope); err != nil {
+ return fmt.Errorf("cannot update task rank: %w", err)
+ }
+ }
+
return nil
},
)
diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql
index db05229c8..08e8aec4e 100644
--- a/pkg/server/api/console/v1/schema.graphql
+++ b/pkg/server/api/console/v1/schema.graphql
@@ -69,6 +69,26 @@ enum TaskState @goModel(model: "go.probo.inc/probo/pkg/coredata.TaskState") {
DONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateDone")
}
+enum TaskPriority
+ @goModel(model: "go.probo.inc/probo/pkg/coredata.TaskPriority") {
+ URGENT
+ @goEnum(
+ value: "go.probo.inc/probo/pkg/coredata.TaskPriorityUrgent"
+ )
+ HIGH
+ @goEnum(
+ value: "go.probo.inc/probo/pkg/coredata.TaskPriorityHigh"
+ )
+ MEDIUM
+ @goEnum(
+ value: "go.probo.inc/probo/pkg/coredata.TaskPriorityMedium"
+ )
+ LOW
+ @goEnum(
+ value: "go.probo.inc/probo/pkg/coredata.TaskPriorityLow"
+ )
+}
+
enum EvidenceState
@goModel(model: "go.probo.inc/probo/pkg/coredata.EvidenceState") {
FULFILLED
@@ -507,7 +527,7 @@ enum MeasureOrderField
enum TaskOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.TaskOrderField") {
- PRIORITY
+ PRIORITY_RANK
CREATED_AT
}
@@ -2404,7 +2424,8 @@ type Task implements Node {
name: String!
description: String
state: TaskState!
- priority: Int!
+ priority: TaskPriority!
+ rank: Int!
timeEstimate: Duration
deadline: Datetime
assignedTo: Profile @goField(forceResolver: true)
@@ -4274,6 +4295,7 @@ input CreateTaskInput {
measureId: ID
name: String!
description: String
+ priority: TaskPriority!
timeEstimate: Duration
assignedToId: ID
deadline: Datetime
@@ -4284,7 +4306,8 @@ input UpdateTaskInput {
name: String
description: String @goField(omittable: true)
state: TaskState
- priority: Int
+ priority: TaskPriority
+ rank: Int
timeEstimate: Duration @goField(omittable: true)
deadline: Datetime @goField(omittable: true)
assignedToId: ID @goField(omittable: true)
diff --git a/pkg/server/api/console/v1/types/task.go b/pkg/server/api/console/v1/types/task.go
index 4aa91d63c..3965df689 100644
--- a/pkg/server/api/console/v1/types/task.go
+++ b/pkg/server/api/console/v1/types/task.go
@@ -71,6 +71,7 @@ func NewTask(t *coredata.Task) *Task {
Description: t.Description,
State: t.State,
Priority: t.Priority,
+ Rank: t.Rank,
TimeEstimate: t.TimeEstimate,
Deadline: t.Deadline,
CreatedAt: t.CreatedAt,
diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go
index eca97c6a7..990e3086e 100644
--- a/pkg/server/api/console/v1/v1_resolver.go
+++ b/pkg/server/api/console/v1/v1_resolver.go
@@ -4061,6 +4061,7 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas
OrganizationID: input.OrganizationID,
Name: input.Name,
Description: input.Description,
+ Priority: input.Priority,
TimeEstimate: input.TimeEstimate,
AssignedToID: input.AssignedToID,
Deadline: input.Deadline,
@@ -4099,6 +4100,7 @@ func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTas
Description: gqlutils.UnwrapOmittable(input.Description),
State: input.State,
Priority: input.Priority,
+ Rank: input.Rank,
TimeEstimate: gqlutils.UnwrapOmittable(input.TimeEstimate),
Deadline: gqlutils.UnwrapOmittable(input.Deadline),
AssignedToID: gqlutils.UnwrapOmittable(input.AssignedToID),
diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go
index d6c8ffa1d..9a37d49db 100644
--- a/pkg/server/api/mcp/v1/schema.resolvers.go
+++ b/pkg/server/api/mcp/v1/schema.resolvers.go
@@ -1896,6 +1896,7 @@ func (r *Resolver) UpdateTaskTool(ctx context.Context, req *mcp.CallToolRequest,
Description: UnwrapOmittable(input.Description),
State: input.State,
Priority: input.Priority,
+ Rank: input.Rank,
TimeEstimate: UnwrapOmittable(input.TimeEstimate),
Deadline: UnwrapOmittable(input.Deadline),
AssignedToID: UnwrapOmittable(input.AssignedToID),
diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml
index 3bb5aa685..a3b25c49d 100644
--- a/pkg/server/api/mcp/v1/specification.yaml
+++ b/pkg/server/api/mcp/v1/specification.yaml
@@ -4621,7 +4621,7 @@ components:
TaskOrderField:
type: string
enum:
- - PRIORITY
+ - PRIORITY_RANK
- CREATED_AT
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.TaskOrderField
@@ -4638,6 +4638,15 @@ components:
$ref: "#/components/schemas/OrderDirection"
description: Task order direction
+ TaskPriority:
+ type: string
+ enum:
+ - URGENT
+ - HIGH
+ - MEDIUM
+ - LOW
+ go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.TaskPriority
+
Task:
type: object
required:
@@ -4646,6 +4655,7 @@ components:
- name
- state
- priority
+ - rank
- created_at
- updated_at
properties:
@@ -4676,8 +4686,11 @@ components:
$ref: "#/components/schemas/TaskState"
description: Task state
priority:
+ $ref: "#/components/schemas/TaskPriority"
+ description: Task priority level
+ rank:
type: integer
- description: Task priority within state
+ description: Task rank within state
time_estimate:
anyOf:
- $ref: "#/components/schemas/Duration"
@@ -4830,8 +4843,15 @@ components:
description: No state
description: Task state
priority:
+ anyOf:
+ - $ref: "#/components/schemas/TaskPriority"
+ description: Task priority level
+ - type: "null"
+ description: No priority
+ description: Task priority level
+ rank:
type: integer
- description: Task priority within state
+ description: Task rank within state
time_estimate:
anyOf:
- $ref: "#/components/schemas/Duration"
diff --git a/pkg/server/api/mcp/v1/types/task.go b/pkg/server/api/mcp/v1/types/task.go
index c1828489e..c14b33efc 100644
--- a/pkg/server/api/mcp/v1/types/task.go
+++ b/pkg/server/api/mcp/v1/types/task.go
@@ -26,6 +26,7 @@ func NewTask(t *coredata.Task) *Task {
Description: t.Description,
State: t.State,
Priority: t.Priority,
+ Rank: t.Rank,
TimeEstimate: t.TimeEstimate,
CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt,