Skip to content

Commit cbbacc5

Browse files
authored
Merge pull request #1040 from Chris0Jeky/paper/1022-conflicts
Add proposal conflict detector for review UI (#1022)
2 parents edfaab6 + b194407 commit cbbacc5

21 files changed

Lines changed: 2158 additions & 30 deletions

backend/src/Taskdeck.Api/Controllers/AutomationProposalsController.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class AutomationProposalsController : AuthenticatedControllerBase
2929
private readonly IAutomationExecutorService _executorService;
3030
private readonly ISimilarDecisionService _similarDecisionService;
3131
private readonly BoardAuthorizationService _authorizationService;
32+
private readonly IProposalConflictDetector _conflictDetector;
3233
private readonly IProvenanceQueryService _provenanceQueryService;
3334
private readonly IConfidenceBreakdownService _confidenceBreakdownService;
3435
private readonly ICardHistoryService _cardHistoryService;
@@ -39,6 +40,7 @@ public AutomationProposalsController(
3940
IAutomationExecutorService executorService,
4041
ISimilarDecisionService similarDecisionService,
4142
BoardAuthorizationService authorizationService,
43+
IProposalConflictDetector conflictDetector,
4244
IProvenanceQueryService provenanceQueryService,
4345
IConfidenceBreakdownService confidenceBreakdownService,
4446
ICardHistoryService cardHistoryService,
@@ -49,6 +51,7 @@ public AutomationProposalsController(
4951
_executorService = executorService;
5052
_similarDecisionService = similarDecisionService;
5153
_authorizationService = authorizationService;
54+
_conflictDetector = conflictDetector;
5255
_provenanceQueryService = provenanceQueryService;
5356
_confidenceBreakdownService = confidenceBreakdownService;
5457
_cardHistoryService = cardHistoryService;
@@ -277,6 +280,23 @@ public async Task<IActionResult> DismissProposals(
277280
: result.ToErrorActionResult();
278281
}
279282

283+
/// <summary>
284+
/// Gets tone-classified conflict/warning/status rows for a proposal.
285+
/// </summary>
286+
[HttpGet("{id}/conflicts")]
287+
public async Task<IActionResult> GetProposalConflicts(Guid id, CancellationToken cancellationToken = default)
288+
{
289+
if (!TryGetCurrentUserId(out var callerUserId, out var errorResult))
290+
return errorResult!;
291+
292+
var auth = await AuthorizeProposalAsync(id, callerUserId, requireWriteAccess: false, cancellationToken);
293+
if (auth.ErrorResult is not null)
294+
return auth.ErrorResult;
295+
296+
var result = await _conflictDetector.DetectConflictsAsync(id, callerUserId, cancellationToken);
297+
return result.IsSuccess ? Ok(result.Value) : result.ToErrorActionResult();
298+
}
299+
280300
/// <summary>
281301
/// Gets the card history ledger for a proposal, showing all touches on affected cards.
282302
/// </summary>
@@ -311,7 +331,6 @@ public async Task<IActionResult> GetProposalSideEffects(Guid id, CancellationTok
311331
var result = await _sideEffectAnalyzer.AnalyzeAsync(id, cancellationToken);
312332
return result.IsSuccess ? Ok(result.Value) : result.ToErrorActionResult();
313333
}
314-
315334
/// <summary>
316335
/// Gets a diff preview for a proposal showing what changes will be made.
317336
/// </summary>

backend/src/Taskdeck.Api/Extensions/ApplicationServiceRegistration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
4848
services.AddScoped<HistoryService>();
4949
services.AddScoped<IHistoryService>(sp => sp.GetRequiredService<HistoryService>());
5050
services.AddScoped<IAutomationProposalService, AutomationProposalService>();
51+
services.AddScoped<IProposalConflictDetector, ProposalConflictDetector>();
5152
services.AddScoped<IProvenanceQueryService, ProvenanceQueryService>();
5253
services.AddScoped<IConfidenceBreakdownService, ConfidenceBreakdownService>();
5354
services.AddScoped<ICardHistoryService, CardHistoryService>();
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Taskdeck.Domain.Entities;
2+
3+
namespace Taskdeck.Application.DTOs;
4+
5+
/// <summary>
6+
/// DTO for a single conflict/warning/status row returned by the conflict detector.
7+
/// </summary>
8+
public record ConflictRowDto(
9+
ConflictTone Tone,
10+
string Key,
11+
string Value
12+
)
13+
{
14+
public static ConflictRowDto FromDomain(ConflictRow row)
15+
{
16+
return new ConflictRowDto(row.Tone, row.Key, row.Value);
17+
}
18+
}

backend/src/Taskdeck.Application/Interfaces/IAutomationProposalRepository.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public interface IAutomationProposalRepository : IRepository<AutomationProposal>
2020
string actionType,
2121
ProposalSourceType sourceType,
2222
CancellationToken cancellationToken = default);
23+
Task<IReadOnlyList<AutomationProposal>> GetPendingByOperationTargetAsync(string targetType, string targetId, CancellationToken cancellationToken = default);
2324
Task<IEnumerable<AutomationProposal>> GetExpiredAsync(CancellationToken cancellationToken = default);
2425

2526
/// <summary>

backend/src/Taskdeck.Application/Interfaces/ICardCommentRepository.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ namespace Taskdeck.Application.Interfaces;
55
public interface ICardCommentRepository : IRepository<CardComment>
66
{
77
Task<IEnumerable<CardComment>> GetByCardIdAsync(Guid cardId, CancellationToken cancellationToken = default);
8+
Task<int> CountByCardIdAsync(Guid cardId, CancellationToken cancellationToken = default);
89
Task<CardComment?> GetByIdWithMentionsAsync(Guid id, CancellationToken cancellationToken = default);
910
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Taskdeck.Application.DTOs;
2+
using Taskdeck.Domain.Common;
3+
4+
namespace Taskdeck.Application.Services;
5+
6+
/// <summary>
7+
/// Detects conflicts, warnings, and status signals for a proposal.
8+
/// Returns a tone-classified list of rows for the review UI.
9+
/// </summary>
10+
public interface IProposalConflictDetector
11+
{
12+
Task<Result<IReadOnlyList<ConflictRowDto>>> DetectConflictsAsync(
13+
Guid proposalId,
14+
Guid userId,
15+
CancellationToken cancellationToken = default);
16+
}

0 commit comments

Comments
 (0)