[WIP] Plan implementation for backend documentation updates#20
[WIP] Plan implementation for backend documentation updates#20Chris0Jeky merged 30 commits intomainfrom
Conversation
Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
… bugs Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
Updated all new domain entities to use Guid for ID fields to match Entity base class: - AutomationProposal: BoardId, RequestedByUserId, DecidedByUserId - AutomationProposalOperation: ProposalId - ArchiveItem: EntityId, BoardId, ArchivedByUserId, RestoredByUserId - ChatSession: UserId, BoardId - ChatMessage: SessionId, ProposalId - CommandRun: RequestedByUserId - CommandRunLog: CommandRunId Also updated repository interfaces and implementations to use Guid parameters. CorrelationId remains string as it's not an entity reference. Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
- Created IAutomationProposalService interface with all required operations - Implemented AutomationProposalService using Result pattern and IUnitOfWork - Created AutomationProposalDtos with proposal and operation DTOs - Added comprehensive test suite with 18 passing tests - All 338 tests in solution pass Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
- Created IArchiveRecoveryService interface with archive CRUD operations - Implemented ArchiveRecoveryService with restore logic for boards, columns, and cards - Added DTOs: CreateArchiveItemDto, ArchiveItemDto, RestoreArchiveItemDto, RestoreResult - Implemented conflict strategies: Fail, Rename, AppendSuffix - Added permission checks via IAuthorizationService - Created audit log entries for archive operations - Implemented snapshot serialization/deserialization for entity restoration - Added 26 comprehensive unit tests covering all scenarios - All tests pass (364 total tests across all projects) Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
…ion tests Co-authored-by: Chris0Jeky <59696583+Chris0Jeky@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds backend support for “automation proposals” and “archive recovery” workflows (domain models, persistence, services, controllers, and tests) to enable proposing, approving/rejecting, previewing diffs, and restoring archived entities.
Changes:
- Introduces new domain entities + EF Core persistence (DbContext, configurations, repositories, migrations) for automation proposals, archive items, chat sessions/messages, and command runs/logs.
- Adds application services (proposal lifecycle, policy engine, instruction planner, executor, archive recovery) and exposes new REST endpoints via
AutomationProposalsControllerandArchiveController. - Adds extensive unit/integration test coverage for the new services and API endpoints.
Reviewed changes
Copilot reviewed 56 out of 57 changed files in this pull request and generated 24 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/tests/Taskdeck.Application.Tests/Services/AutomationProposalServiceTests.cs | Unit tests for proposal lifecycle operations (create/get/approve/reject/apply/fail/expire/diff/filter). |
| backend/tests/Taskdeck.Application.Tests/Services/AutomationPolicyEngineTests.cs | Unit tests for risk classification and policy/permission validation rules. |
| backend/tests/Taskdeck.Application.Tests/Services/AutomationPlannerServiceTests.cs | Unit tests for parsing instructions into proposal operations. |
| backend/tests/Taskdeck.Application.Tests/Services/AutomationExecutorServiceTests.cs | Unit tests for execute preconditions (idempotency key, approved status, policy/permission failures). |
| backend/tests/Taskdeck.Application.Tests/Services/ArchiveRecoveryServiceTests.cs | Unit tests for archive item creation, listing, and restore scenarios (conflicts, permissions, WIP limits). |
| backend/tests/Taskdeck.Api.Tests/AutomationProposalsApiTests.cs | Integration tests for automation proposal REST endpoints. |
| backend/tests/Taskdeck.Api.Tests/ArchiveApiTests.cs | Integration tests for archive REST endpoints. |
| backend/src/Taskdeck.Infrastructure/Taskdeck.Infrastructure.csproj | Adds package reference related to HTTP context access in Infrastructure. |
| backend/src/Taskdeck.Infrastructure/Repositories/UnitOfWork.cs | Extends UnitOfWork to expose new repositories (automation, archive, chat, command runs). |
| backend/src/Taskdeck.Infrastructure/Repositories/CommandRunRepository.cs | Adds repository queries for command runs (by user/status/template/correlation, with logs). |
| backend/src/Taskdeck.Infrastructure/Repositories/ChatSessionRepository.cs | Adds repository queries for chat sessions (by user/board/status, with messages). |
| backend/src/Taskdeck.Infrastructure/Repositories/ChatMessageRepository.cs | Adds repository queries for chat messages (by session/proposal). |
| backend/src/Taskdeck.Infrastructure/Repositories/AutomationProposalRepository.cs | Adds repository queries for proposals (filters + expired). |
| backend/src/Taskdeck.Infrastructure/Repositories/ArchiveItemRepository.cs | Adds repository queries for archive items (filters + by entity). |
| backend/src/Taskdeck.Infrastructure/Persistence/TaskdeckDbContext.cs | Adds DbSets for new entities. |
| backend/src/Taskdeck.Infrastructure/Persistence/Configurations/CommandRunLogConfiguration.cs | EF mapping for command run logs. |
| backend/src/Taskdeck.Infrastructure/Persistence/Configurations/CommandRunConfiguration.cs | EF mapping for command runs and logs relationship. |
| backend/src/Taskdeck.Infrastructure/Persistence/Configurations/ChatSessionConfiguration.cs | EF mapping for chat sessions and messages relationship. |
| backend/src/Taskdeck.Infrastructure/Persistence/Configurations/ChatMessageConfiguration.cs | EF mapping for chat messages. |
| backend/src/Taskdeck.Infrastructure/Persistence/Configurations/AutomationProposalOperationConfiguration.cs | EF mapping for proposal operations (including unique idempotency key). |
| backend/src/Taskdeck.Infrastructure/Persistence/Configurations/AutomationProposalConfiguration.cs | EF mapping for proposals and operations relationship. |
| backend/src/Taskdeck.Infrastructure/Persistence/Configurations/ArchiveItemConfiguration.cs | EF mapping for archive items. |
| backend/src/Taskdeck.Infrastructure/Migrations/TaskdeckDbContextModelSnapshot.cs | Updates EF snapshot for new entities. |
| backend/src/Taskdeck.Infrastructure/Migrations/20260212200649_AddAutomationArchiveChatOpsEntities.cs | Adds migration creating new tables and indexes. |
| backend/src/Taskdeck.Infrastructure/Migrations/20260212200649_AddAutomationArchiveChatOpsEntities.Designer.cs | Migration designer output for new entities. |
| backend/src/Taskdeck.Infrastructure/Identity/UserContext.cs | Adds IUserContext implementation reading claims from HttpContext. |
| backend/src/Taskdeck.Infrastructure/DependencyInjection.cs | Registers new repositories in Infrastructure DI. |
| backend/src/Taskdeck.Domain/Exceptions/DomainException.cs | Adds ErrorCodes.InvalidOperation. |
| backend/src/Taskdeck.Domain/Entities/CommandRunLog.cs | New domain entity for command run logs. |
| backend/src/Taskdeck.Domain/Entities/CommandRun.cs | New domain entity for command runs + state transitions. |
| backend/src/Taskdeck.Domain/Entities/ChatSession.cs | New domain entity for chat sessions. |
| backend/src/Taskdeck.Domain/Entities/ChatMessage.cs | New domain entity for chat messages. |
| backend/src/Taskdeck.Domain/Entities/AutomationProposalOperation.cs | New domain entity for proposal operations. |
| backend/src/Taskdeck.Domain/Entities/AutomationProposal.cs | New domain entity for proposals + lifecycle rules. |
| backend/src/Taskdeck.Domain/Entities/ArchiveItem.cs | New domain entity for archive items + restore status transitions. |
| backend/src/Taskdeck.Application/Services/IAutomationProposalService.cs | New service interface for proposal lifecycle APIs. |
| backend/src/Taskdeck.Application/Services/IAutomationPolicyEngine.cs | New service interface for risk/policy/permission validation. |
| backend/src/Taskdeck.Application/Services/IAutomationPlannerService.cs | New service interface for instruction parsing -> proposals. |
| backend/src/Taskdeck.Application/Services/IAutomationExecutorService.cs | New service interface for executing proposals. |
| backend/src/Taskdeck.Application/Services/IArchiveRecoveryService.cs | New service interface for archive creation/listing/restoration. |
| backend/src/Taskdeck.Application/Services/AutomationProposalService.cs | Implements proposal lifecycle operations using UnitOfWork + domain rules. |
| backend/src/Taskdeck.Application/Services/AutomationPolicyEngine.cs | Implements risk classification and policy/permission validation. |
| backend/src/Taskdeck.Application/Services/AutomationPlannerService.cs | Parses simple instruction patterns into proposal operations. |
| backend/src/Taskdeck.Application/Services/AutomationExecutorService.cs | Executes proposal operations via existing Board/Card/Column services with transactions + auditing. |
| backend/src/Taskdeck.Application/Services/ArchiveRecoveryService.cs | Implements archive creation, listing, and restore flows with conflict strategies. |
| backend/src/Taskdeck.Application/Interfaces/IUserContext.cs | Defines claim-based actor identity abstraction and guidance. |
| backend/src/Taskdeck.Application/Interfaces/IUnitOfWork.cs | Extends UnitOfWork interface with new repositories. |
| backend/src/Taskdeck.Application/Interfaces/ICommandRunRepository.cs | Repository interface for command runs. |
| backend/src/Taskdeck.Application/Interfaces/IChatSessionRepository.cs | Repository interface for chat sessions. |
| backend/src/Taskdeck.Application/Interfaces/IChatMessageRepository.cs | Repository interface for chat messages. |
| backend/src/Taskdeck.Application/Interfaces/IAutomationProposalRepository.cs | Repository interface for proposals. |
| backend/src/Taskdeck.Application/Interfaces/IArchiveItemRepository.cs | Repository interface for archive items. |
| backend/src/Taskdeck.Application/DTOs/AutomationProposalDtos.cs | DTOs for proposals, operations, filters, status updates. |
| backend/src/Taskdeck.Application/DTOs/ArchiveRecoveryDtos.cs | DTOs for archive items and restore options/results. |
| backend/src/Taskdeck.Api/Program.cs | Registers new services and adds IUserContext + HttpContext accessor. |
| backend/src/Taskdeck.Api/Controllers/AutomationProposalsController.cs | New automation proposals REST endpoints. |
| backend/src/Taskdeck.Api/Controllers/ArchiveController.cs | New archive listing and restore endpoints. |
Files not reviewed (1)
- backend/src/Taskdeck.Infrastructure/Migrations/20260212200649_AddAutomationArchiveChatOpsEntities.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
backend/src/Taskdeck.Application/Interfaces/IAutomationProposalRepository.cs
Outdated
Show resolved
Hide resolved
| string entityType, | ||
| Guid entityId, | ||
| [FromQuery] Guid restoredByUserId, | ||
| [FromBody] RestoreArchiveItemDto dto, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| // Find the archive item by entity type and ID | ||
| var archiveItems = await _archiveService.GetArchiveItemsAsync( | ||
| entityType, | ||
| null, | ||
| null, | ||
| 1000, | ||
| cancellationToken); |
There was a problem hiding this comment.
entityType is used as-is for filtering, but ArchiveItem enforces lowercase values ("board", "column", "card"). Routes/docs suggest values like "Card", which will never match and makes restore brittle/case-sensitive. Normalize entityType (e.g., ToLowerInvariant()) and/or validate against the allowed set before querying.
backend/src/Taskdeck.Application/Services/AutomationExecutorService.cs
Outdated
Show resolved
Hide resolved
| foreach (var opDto in dto.Operations) | ||
| { | ||
| var operation = new AutomationProposalOperation( | ||
| proposal.Id, | ||
| opDto.Sequence, | ||
| opDto.ActionType, | ||
| opDto.TargetType, | ||
| opDto.Parameters, | ||
| opDto.IdempotencyKey, | ||
| opDto.TargetId, | ||
| opDto.ExpectedVersion); | ||
|
|
||
| proposal.AddOperation(operation); | ||
| } |
There was a problem hiding this comment.
This foreach loop immediately maps its iteration variable to another variable - consider mapping the sequence explicitly using '.Select(...)'.
| if (boardId.HasValue && operation.TargetType.Equals("card", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(operation.TargetId)) | ||
| { | ||
| if (Guid.TryParse(operation.TargetId, out var cardId)) | ||
| { | ||
| var card = await _unitOfWork.Cards.GetByIdAsync(cardId, cancellationToken); | ||
| if (card != null && card.BoardId != boardId.Value) | ||
| return Result.Failure(ErrorCodes.Forbidden, $"Card {cardId} does not belong to board {boardId}"); | ||
| } |
There was a problem hiding this comment.
These 'if' statements can be combined.
| if (boardId.HasValue && operation.TargetType.Equals("card", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(operation.TargetId)) | |
| { | |
| if (Guid.TryParse(operation.TargetId, out var cardId)) | |
| { | |
| var card = await _unitOfWork.Cards.GetByIdAsync(cardId, cancellationToken); | |
| if (card != null && card.BoardId != boardId.Value) | |
| return Result.Failure(ErrorCodes.Forbidden, $"Card {cardId} does not belong to board {boardId}"); | |
| } | |
| if (boardId.HasValue | |
| && operation.TargetType.Equals("card", StringComparison.OrdinalIgnoreCase) | |
| && !string.IsNullOrEmpty(operation.TargetId) | |
| && Guid.TryParse(operation.TargetId, out var cardId)) | |
| { | |
| var card = await _unitOfWork.Cards.GetByIdAsync(cardId, cancellationToken); | |
| if (card != null && card.BoardId != boardId.Value) | |
| return Result.Failure(ErrorCodes.Forbidden, $"Card {cardId} does not belong to board {boardId}"); |
| try | ||
| { | ||
| var operations = new List<CreateProposalOperationDto>(); | ||
| var instructionLower = instruction.ToLowerInvariant(); |
There was a problem hiding this comment.
This assignment to instructionLower is useless, since its value is never read.
| var instructionLower = instruction.ToLowerInvariant(); |
Add an optional int limit (default 100) to IAutomationProposalRepository.GetByBoardIdAsync to match other query methods and allow limiting the number of returned proposals. The CancellationToken parameter is unchanged.
Introduce GetArchiveItemByEntityAsync to IArchiveRecoveryService to allow fetching an archived item by entity type and ID. The method returns Task<Result<ArchiveItemDto>> and accepts a CancellationToken, enabling lookups by entity attributes for restore or display flows.
Eager-load the Operations navigation property across AutomationProposal repository queries to avoid N+1 issues. Adds an overridden GetByIdAsync that includes Operations, and adds a limit parameter (default 100) to GetByBoardIdAsync with Take(limit) for consistency with other query methods.
Introduce a local limit (default 100 when filter.Limit <= 0) and pass it to repository queries to avoid unbounded result sets. Simplify in-memory filtering by applying each optional filter (Status, BoardId, UserId, RiskLevel) independently rather than only when combined with Status, and cap the final results with Take(limit). Small comment and formatting tweaks in AutomationProposalService.
Add input validation for list parameters (limit range 1..1000) and normalize/validate entityType. Introduce GetArchiveItemByEntityAsync to look up archive items by entity type+ID with validation and not-found handling. Fix authorization handling to propagate errors, and move target-board existence/archived checks into the column/card restore branches so restoring to archived or missing boards is prevented. Improve overall error messages for invalid inputs.
Remove the in-memory _executedOperations dictionary and its contains/add checks. After loading a proposal, return success if proposal.Status == ProposalStatus.Applied to provide idempotent behavior across requests/processes. This avoids relying on transient in-memory state (which could leak memory and fails in multi-process deployments) while preserving existing validation and workflow checks.
Enforce authorization on AutomationProposalsController and inject IAutomationExecutorService and IUserContext. Use a TryGetCurrentUserId helper to populate requested/decidedByUserId for Create/Approve/Reject flows and centralize authentication error responses. Add idempotency (Idempotency-Key) validation and delegate execution to the executor service, then return the updated proposal via GetProposalById. Expand error mapping (including InvalidOperation, AuthenticationFailed, Unauthorized, Forbidden) for consistent HTTP responses.
Enforce authorization on archive endpoints and switch RestoreArchivedItem to use the authenticated user from IUserContext instead of a query param. Validate and normalize entityType (board/column/card), call GetArchiveItemByEntityAsync, and add robust error handling for authentication/authorization/forbidden/invalid operations. Introduces helper methods TryNormalizeEntityType and TryGetCurrentUserId and injects IUserContext into the controller.
Register AutomationPlannerService and AutomationExecutorService against their interfaces (IAutomationPlannerService, IAutomationExecutorService) in Program.cs instead of registering the concrete types directly. This ties the DI container to abstractions, improving testability and allowing easier implementation swaps.
Update AutomationProposalServiceTests to match the updated GetByBoardIdAsync signature by adding the page size argument (100) to the mock setup. This ensures the test mock matches the method's new parameters and returns the expected proposals.
Add a scoped registration in Program.cs that maps IAuthorizationService to the existing AuthorizationService using GetRequiredService. This allows components to depend on the IAuthorizationService interface and reuse the already-registered AuthorizationService implementation.
Update automation API tests to register/authenticate test users and create owned boards before exercising endpoints. Add AuthenticateAsync and CreateOwnedBoardAsync helpers and include Authorization header. Remove decidedByUserId query param from approve/reject calls, send Idempotency-Key header when executing proposals, and update expected errorCode from "Conflict" to "InvalidOperation". Also adjust proposal action payload to target a board (include TargetId) and add System.Net.Http.Headers import.
Add AuthenticateAsync helper to register a test user, obtain a token and set the Authorization header for the test HttpClient. Update archive tests to call AuthenticateAsync before requests and adjust the restore test request path (removed restoredByUserId query). Clean up unused usings and add System.Net.Http.Headers for setting the bearer token.
Move OrderByDescending(p => p.CreatedAt).Take(limit) to after ToListAsync in repository methods (GetByStatusAsync, GetByBoardIdAsync, GetByUserIdAsync, GetByRiskLevelAsync). The queries now materialize results first (preserving Include(p => p.Operations)) and then apply in-memory ordering/limiting to avoid EF translation/client-evaluation issues while keeping the external behavior and limit parameter.
If an explicit DiffPreview exists (non-whitespace) return it; otherwise generate a human-readable preview from the proposal's Operations. Operations are ordered by Sequence and formatted as "{Sequence}. {ActionType} {TargetType[:TargetId]}" joined with newlines. If no operations are present, the method still returns the existing "Diff preview not available" failure. Also tightened the DiffPreview check to IsNullOrWhiteSpace.
Adds a unit test that verifies the restore endpoint returns BadRequest when an invalid entity type is used. The test authenticates, posts a RestoreArchiveItemDto to /api/archive/not-a-type/{id}/restore and asserts a 400 response with errorCode "ValidationError". This ensures the API validates entity type inputs for archive restore operations.
Add two integration tests to AutomationProposalsApiTests: - ExecuteProposal_WithoutIdempotencyKey_ShouldReturnBadRequest: approves a proposal then attempts to execute it without an idempotency key and asserts a 400 BadRequest with errorCode "ValidationError". - ApproveProposal_ShouldReturnUnauthorized_WhenNotAuthenticated: clears auth header and asserts approving a proposal returns 401 Unauthorized. These tests exercise validation and authentication behavior for the automation proposals endpoints.
Add a unit test RestoreArchiveItemAsync_ShouldReturnFailure_WhenAuthorizationCheckFails to ArchiveRecoveryServiceTests. The test sets up an archived item and mocks the authorization service to return a NotFound error for CanWriteBoardAsync, then asserts the service returns a failure with ErrorCode NotFound and an error message indicating the board is missing. This covers the authorization failure path in RestoreArchiveItemAsync.
…kend-docs [WIP] Plan implementation for backend documentation updates Remaining gap vs full backend docs: chat/ops/log-stream/worker activation is still pending implementation
…805) Pre-resolve expected merge conflicts across all 10 platform expansion PRs: - STATUS.md: consolidated wave summary, new CI workflow entries - IMPLEMENTATION_MASTERPLAN.md: delivery entry #130, next-steps item #20 - TESTING_GUIDE.md: cross-browser, visual regression, mutation, Testcontainers - decisions/INDEX.md: canonical ADR numbering (0023-0027) resolving the 5-way ADR-0023 collision across PRs
Backend Activation - Remaining Implementation
Based on the problem statement and reviewing what's been completed, here's the plan to finish the end-to-end implementation:
Completed So Far ✅
Wave 2: Missing Backend Surfaces (Remaining)
Wave 3: Runtime and Reliability
Wave 4: Testing and Integration
Wave 5: Documentation
Current Focus: Implementing Wave 2 (chat, ops, logs services and controllers)
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.