diff --git a/client/src/pages/BoardPage.tsx b/client/src/pages/BoardPage.tsx
index 5080f34..39dab93 100644
--- a/client/src/pages/BoardPage.tsx
+++ b/client/src/pages/BoardPage.tsx
@@ -8,7 +8,7 @@ import {
useSensors,
type DragEndEvent,
} from '@dnd-kit/core'
-import { Bot, Plus } from 'lucide-react'
+import { Bot, Plus, Trash2 } from 'lucide-react'
import { toast } from 'sonner'
import { AppShell } from '@/components/AppShell'
import { Badge } from '@/components/ui/badge'
@@ -554,6 +554,21 @@ function TaskDrawer({
)}
+
+
+
+
)
diff --git a/src/Modules/TeamUp.Modules.OrgBoard/Domain/WorkItem.cs b/src/Modules/TeamUp.Modules.OrgBoard/Domain/WorkItem.cs
index a147fb0..0c7ec41 100644
--- a/src/Modules/TeamUp.Modules.OrgBoard/Domain/WorkItem.cs
+++ b/src/Modules/TeamUp.Modules.OrgBoard/Domain/WorkItem.cs
@@ -69,6 +69,9 @@ internal sealed class WorkItem : Entity
UpdatedAtUtc = nowUtc;
}
+ /// Detach from a parent (used when the parent is deleted, so the child stays on the board).
+ public void ClearParent() => ParentId = null;
+
/// Appends an approved agent artifact (spec / test plan) to the task.
public void AttachArtifact(string content, DateTimeOffset nowUtc)
{
diff --git a/src/Modules/TeamUp.Modules.OrgBoard/Endpoints/OrgBoardEndpoints.cs b/src/Modules/TeamUp.Modules.OrgBoard/Endpoints/OrgBoardEndpoints.cs
index bdac069..11d064a 100644
--- a/src/Modules/TeamUp.Modules.OrgBoard/Endpoints/OrgBoardEndpoints.cs
+++ b/src/Modules/TeamUp.Modules.OrgBoard/Endpoints/OrgBoardEndpoints.cs
@@ -30,6 +30,7 @@ internal static class OrgBoardEndpoints
group.MapGet("/board", GetBoard).RequireAuthorization();
group.MapPatch("/tasks/{id:guid}/move", MoveTask).RequireAuthorization();
group.MapPatch("/tasks/{id:guid}/assign", AssignTask).RequireAuthorization();
+ group.MapDelete("/tasks/{id:guid}", DeleteTask).RequireAuthorization();
group.MapGet("/cartable", Cartable).RequireAuthorization();
group.MapPost("/seats", CreateSeat).RequireAuthorization();
@@ -273,6 +274,36 @@ internal static class OrgBoardEndpoints
return Results.Ok(ToResponse(item));
}
+ // Remove a task from the board. Its children are detached (kept as top-level) rather than deleted,
+ // and its status-transition history is dropped. Any agent runs/reviews it spawned are left as history.
+ private static async Task DeleteTask(
+ Guid id, ICurrentUser user, IPermissionService permissions,
+ IAuditLog audit, OrgBoardDbContext db, CancellationToken ct)
+ {
+ var item = await db.WorkItems.FirstOrDefaultAsync(w => w.Id == id, ct);
+ if (item is null)
+ {
+ return Results.NotFound();
+ }
+
+ var team = await db.Teams.FirstOrDefaultAsync(t => t.Id == item.TeamId, ct);
+ if (team is null || !permissions.Has(Capability.WorkTasks, ScopeRef.Team(item.TeamId), ScopeRef.Org(team.OrganizationId)))
+ {
+ return Results.Forbid();
+ }
+
+ foreach (var child in await db.WorkItems.Where(w => w.ParentId == id).ToListAsync(ct))
+ {
+ child.ClearParent();
+ }
+
+ db.Transitions.RemoveRange(await db.Transitions.Where(t => t.WorkItemId == id).ToListAsync(ct));
+ db.WorkItems.Remove(item);
+ await db.SaveChangesAsync(ct);
+ await audit.WriteAsync(new AuditEvent("task.deleted", "WorkItem", id, user.MemberId, item.Title), ct);
+ return Results.NoContent();
+ }
+
private static async Task GetBoard(
Guid teamId, IPermissionService permissions, OrgBoardDbContext db, CancellationToken ct)
{