Files
nexus/backend/Services/ITaskService.cs
T
devops aef76d5f45
CI - Build & Test / Backend (.NET) (push) Successful in 32s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 18s
CI - Build & Test / Security Check (push) Successful in 2s
CI - Build & Test / Deploy Nexus (push) Has been skipped
feat(board): master-task board, non-destructive stall watchdog, review flow
Board is now a clean master-task view:
- GetBoardAsync returns only top-level (master) tasks; child-tasks render
  nested inside their parent card instead of as separate column cards, so a
  big task split into many sub-tasks stays one card (orphans treated as master)
- New DoneChildTaskCount on the DTO for real progress bars
- Child/detail consumers (GetChildren endpoint, TaskBridgeService) query
  children directly instead of scraping the flat board

Stall watchdog (replaces destructive auto-reset):
- StaleTaskRecoveryService.FlagStalledInProgressTasksAsync marks In-progress
  tasks with no activity past the threshold as stalled (activity event +
  Iris notification) WITHOUT resetting the column — no work is discarded.
  Idempotent: a task is not re-flagged until real progress happens
- BackgroundService now runs this watchdog (TaskRecovery:StalledMinutes=40,
  interval 10m); hard reset kept only on the explicit manual endpoint

Review flow (Bao/Iris only):
- POST tasks/{id}/approve (Review -> Done)
- POST tasks/{id}/request-changes (Review -> target, mandatory comment,
  ExpectedFrom=iris, notifies Iris)

Frontend:
- BoardCard component: master card with ball chip (who has it), progress from
  children, expand to show children grouped by agent with per-child state +
  stalled marker, stalled chip on the master, review action buttons
- Request-changes modal; tasks store approveReview/requestChanges actions

Tests: watchdog flag/idempotency + review threshold; 135 backend tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 22:26:50 +02:00

49 lines
3.4 KiB
C#

using Nexus.Api.Data;
using Nexus.Api.DTOs;
using Nexus.Api.Models;
namespace Nexus.Api.Services;
public enum TaskOperationOutcome { Success, NotFound, InvalidState }
public sealed record TaskOperationResult(TaskOperationOutcome Outcome, WorkTask? Task = null);
public interface ITaskService
{
Task<IReadOnlyList<WorkTask>> GetAllAsync(CancellationToken ct = default);
Task<WorkTask?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<IReadOnlyList<WorkTask>> GetPendingApprovalAsync(CancellationToken ct = default);
Task<WorkTask> CreateAsync(CreateTaskRequest request, CancellationToken ct = default);
Task<TaskOperationResult> ApproveAsync(Guid id, CancellationToken ct = default);
Task<TaskOperationResult> RejectAsync(Guid id, CancellationToken ct = default);
Task<TaskOperationResult> UpdateStateAsync(Guid id, string state, CancellationToken ct = default);
Task<TaskOperationResult> UpdateAsync(Guid id, UpdateTaskRequest request, CancellationToken ct = default);
Task<TaskOperationResult> DeleteAsync(Guid id, CancellationToken ct = default);
// Dashboard-facing task operations
Task<IReadOnlyList<WorkTask>> GetOpenAsync(CancellationToken ct = default);
Task<WorkTask> CreateDashboardTaskAsync(string title, string? detail, string? source, string? priority, string? assignedTo, Guid? parentTaskId = null, CancellationToken ct = default);
Task<WorkTask> CreateAgentTaskAsync(string title, string? detail, string? source, string? priority, string? assignedTo, string? expectedFrom, Guid? parentTaskId = null, bool startsInProgress = true, string? initialState = null, CancellationToken ct = default);
Task<TaskOperationResult> UpdateDashboardTaskAsync(Guid id, string? title, string? detail, string? source, string? priority, string? assignedTo, DateTimeOffset? dueDate = null, CancellationToken ct = default);
Task<TaskOperationResult> UpdateStatusAsync(Guid id, string status, CancellationToken ct = default);
Task<TaskOperationResult> StartCoordinationAsync(Guid id, CancellationToken ct = default);
Task<TaskOperationResult> CompleteViaQueueAsync(Guid id, CancellationToken ct = default);
Task<TaskOperationResult> CyclePriorityAsync(Guid id, CancellationToken ct = default);
// Task Board
Task<BoardResponse> GetBoardAsync(CancellationToken ct = default);
Task<TaskOperationResult> MoveTaskAsync(Guid id, string newState, CancellationToken ct = default);
Task<TaskOperationResult> ApproveReviewAsync(Guid id, CancellationToken ct = default);
Task<TaskOperationResult> RequestChangesAsync(Guid id, string comment, string? targetState, CancellationToken ct = default);
Task<int> ResetStaleAsync(int staleHours, CancellationToken ct = default);
Task<int> ResetStaleInProgressTasksAsync(TimeSpan staleThreshold, CancellationToken ct = default);
Task<IReadOnlyList<WorkTask>> GetChildTasksAsync(Guid parentId, CancellationToken ct = default);
Task<List<DashboardTaskDto>> GetChildTaskDtosAsync(Guid parentId, CancellationToken ct = default);
Task<List<ActivityEvent>> GetTaskActivityAsync(Guid taskId, CancellationToken ct = default);
Task<DashboardTaskDto?> GetDashboardTaskByIdAsync(Guid id, CancellationToken ct = default);
// Agent Workflow Overview
Task<IReadOnlyList<WorkTask>> GetWaitingTasksAsync(CancellationToken ct = default);
Task<AgentWorkflowOverview> GetAgentWorkflowOverviewAsync(TimeSpan staleThreshold, CancellationToken ct = default);
}