Repository navigation
feat(tasks): record a completion date and apply the reopen rule to it (#350) - #378
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Records when a task was completed and applies the reopen rule to that day (#350, part A of the completion-date work in #366).
TaskItem.CompletedAt. It is set on any move to Completed (PUT, PATCH, Blocked to Completed, create as Completed, parent auto-complete) and cleared on reopen. It is written only inTaskRepository.UpdateAsyncandAddAsync. Client-sent values are ignored (no request DTO has the field, and the Detached update path keeps the persisted value).completedAtis returned on task responses, project tasks and journal todos (including child todos).CompletedAtexists, otherwise the linked journal day (unchanged). Tasks with neither are exempt. CodeTASK_REOPEN_PAST_DAY_NOT_ALLOWED, message and 422 status are unchanged;details.assignedDateholds the completion day.TASK_PLAN_COMPLETED_NOT_ALLOWED: PUT or PATCH with ajournalDatethat differs from the current link on a task with aCompletedAtis rejected before any write.AddTaskItemCompletedAtbackfillsCompletedAtfrom the latest genuineCompletedevent that is after the latestReopenedevent (juliandayordering, so both SQLite timestamp formats compare correctly; synthetic "Marked complete by a data migration" events are ignored;Status = 2guard). Tasks with no usable event stay null.UserDateHelper.GetUserDateadded;GetUserTodaydelegates to it.[ProducesResponseType(422)]and XML docs onUpdateandPatch; client regenerated (never hand-edited).docs/API.md,docs/FRONTEND.md,docs/ARCHITECTURE.md(staleIsCompletesample replaced).task-errors.tsmaps the new code; the client-side reopen string is now "Tasks completed on a past day cannot be reopened.";ProjectTaskDto.completedAt. No UI, keyboard or request-body changes.Closes #350
Decisions made during the build
CompletedAtthrough the central write and needs no offset parameter (it is a UTC instant).CompletedAtreturns 422 (reopen first, plan second).CompletedAtstay plannable.CompletedAtbut writes only theTaskCreatedevent.Behavior change to note
A task with a
CompletedAt, no journal link, and a past local completion day can no longer be reopened (previously a task with no link was always reopenable). After the backfill this applies to legacy tasks completed days ago that have a recoverableCompletedevent and no link. The migration runs on startup, so it runs in production on deploy.Files changed
TaskItem,TaskRepository,TaskItemsController,JournalTodosController,ProjectsController, DTOs (newTaskRuleErrorResponseDto),UserDateHelper,UtcDateTimeModelBuilderExtensions, migration20261031024516_AddTaskItemCompletedAt(+ Designer, snapshot),TaskFlow.Api.csproj.types.gen.ts,sdk.gen.ts,index.ts.task-errors.ts(+ test),TodosSection.tsx(+ test),TaskDetailPage.test.tsx,projects.ts.API.md,FRONTEND.md,ARCHITECTURE.md.AddTaskItemCompletedAtMigrationTests, newTaskItemsControllerCompletedAtTests; extended repository, controller, journal, project, UTC-allowlist andUserDateHelpertests.Test results
dotnet build TaskFlow.sln: 0 warnings, 0 errors.dotnet test --project TaskFlow.Api.Tests: 921 passed, 0 failed. CI-style coverage about 95% (floor 75%).dotnet format TaskFlow.sln --verify-no-changes: pass.npm run type-check: pass.npm run test -- --run --coverage: 88 files, 1568 tests passed; 97.31% line coverage (floor 80%).npm run lint: 0 errors, 1 warning atTasksPage.tsx:424(not touched here).Residual findings (for awareness)
CompletedAt. This matches the AC16 wording; if it should be blocked, that is a follow-up (relevant to the completion-day display in feat(journal): show completed tasks on the day they were completed (journal reads and UI) #367).CompletedAtstays null. Conservative and pinned by tests.GenerateDocumentationFile(so the 422 descriptions reach OpenAPI) adds JSDoc blocks, some with raw cref text, to existing DTOs intypes.gen.ts. Cosmetic.TASK_PARENT_CYCLE_NOT_ALLOWEDinFRONTEND.md(not a controller constant), theAPI.mdtitle-max typo (200 vs 100), and the lowercase'completed'the journal toggle sends (accepted by the case-insensitive enum converter).