feat(backend): Implement PendingChange Management (Story 5.10)

Implemented complete Human-in-the-Loop approval workflow for AI-proposed changes:

Changes:
- Created PendingChange DTOs (PendingChangeDto, CreatePendingChangeRequest, ApproveChangeRequest, RejectChangeRequest, PendingChangeFilterDto)
- Implemented IPendingChangeService interface with CRUD, approval/rejection, expiration, and deletion operations
- Implemented PendingChangeService with full workflow support and tenant isolation
- Created McpPendingChangesController REST API with endpoints for listing, approving, rejecting, and deleting pending changes
- Implemented PendingChangeApprovedEventHandler to execute approved changes via MediatR commands (Project, Epic, Story, Task CRUD operations)
- Created PendingChangeExpirationBackgroundService for auto-expiration of changes after 24 hours
- Registered all services and background service in DI container

Technical Details:
- Status flow: PendingApproval → Approved → Applied (or Rejected/Expired)
- Tenant isolation enforced in all operations
- Domain events published for audit trail
- Event-driven execution using MediatR
- Background service runs every 5 minutes to expire old changes
- JWT authentication required for all endpoints

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-09 17:58:12 +01:00
parent debfb95780
commit 2fec2df004
11 changed files with 1182 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
namespace ColaFlow.Modules.Mcp.Application.DTOs;
/// <summary>
/// Request to approve a PendingChange
/// </summary>
public class ApproveChangeRequest
{
// Currently empty, but we may add fields later like:
// - ApprovalComments
// - AutoApply flag
// - ScheduledExecutionTime
}

View File

@@ -0,0 +1,13 @@
using ColaFlow.Modules.Mcp.Domain.ValueObjects;
namespace ColaFlow.Modules.Mcp.Application.DTOs;
/// <summary>
/// Request to create a new PendingChange
/// </summary>
public class CreatePendingChangeRequest
{
public string ToolName { get; set; } = null!;
public DiffPreview Diff { get; set; } = null!;
public int ExpirationHours { get; set; } = 24;
}

View File

@@ -0,0 +1,29 @@
using ColaFlow.Modules.Mcp.Domain.ValueObjects;
namespace ColaFlow.Modules.Mcp.Application.DTOs;
/// <summary>
/// DTO for PendingChange response
/// </summary>
public class PendingChangeDto
{
public Guid Id { get; set; }
public Guid TenantId { get; set; }
public Guid ApiKeyId { get; set; }
public string ToolName { get; set; } = null!;
public DiffPreviewDto Diff { get; set; } = null!;
public string Status { get; set; } = null!;
public DateTime CreatedAt { get; set; }
public DateTime ExpiresAt { get; set; }
public Guid? ApprovedBy { get; set; }
public DateTime? ApprovedAt { get; set; }
public Guid? RejectedBy { get; set; }
public DateTime? RejectedAt { get; set; }
public string? RejectionReason { get; set; }
public DateTime? AppliedAt { get; set; }
public string? ApplicationResult { get; set; }
public bool IsExpired { get; set; }
public bool CanBeApproved { get; set; }
public bool CanBeRejected { get; set; }
public string Summary { get; set; } = null!;
}

View File

@@ -0,0 +1,18 @@
using ColaFlow.Modules.Mcp.Domain.ValueObjects;
namespace ColaFlow.Modules.Mcp.Application.DTOs;
/// <summary>
/// Filter options for querying PendingChanges
/// </summary>
public class PendingChangeFilterDto
{
public PendingChangeStatus? Status { get; set; }
public string? EntityType { get; set; }
public Guid? EntityId { get; set; }
public Guid? ApiKeyId { get; set; }
public string? ToolName { get; set; }
public bool? IncludeExpired { get; set; }
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 20;
}

View File

@@ -0,0 +1,9 @@
namespace ColaFlow.Modules.Mcp.Application.DTOs;
/// <summary>
/// Request to reject a PendingChange
/// </summary>
public class RejectChangeRequest
{
public string Reason { get; set; } = null!;
}