fix(tasks): add a max length to task Description (and record the DTO validation audit)

Open
#365 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
68/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
csharp
Domain
api, backend

Research direction

Start with TaskItemValidator and ProjectValidator to follow the existing description-length rule pattern, then review the controller validation paths noted in the issue. Add validator tests under TaskFlow.Api.Tests/Validators/ and controller tests covering PUT and PATCH rejection with no task changes. Done means all acceptance criteria pass, including POST/PUT/PATCH behavior, and dotnet test plus dotnet format --verify-no-changes pass.

Written by the indexing model from the issue text.

Description

tech-debt

Groomed 2026-10-30 — ready for implementation.

Waits for the monorepo move (#397). This changes the API, so it is built after the assistant moves into this repo and the MCP change ships in the same PR (checked by the integration and error-code tests).

Original Description

Problem

UpdateTaskItemDto and PatchTaskItemDto have fields with no DTO-level validator. #207 (PR #363) added validators for CreateTaskItemDto, UpdateTaskItemDto and PatchTaskItemDto, but each covers only TimezoneOffsetMinutes.

During validation of #207 the reviewer noted that other fields, for example Title length and Description length, are not checked at the DTO level. In PATCH, Title appears to be checked only through the TaskItem model validator (TaskItemValidator) after the changes are applied to the entity.

This has not been verified as a real gap. The TaskItem validator may already cover these fields, in which case the only difference is where the error is raised. This issue is to find out.

What to check
  1. For each field on CreateTaskItemDto, UpdateTaskItemDto and PatchTaskItemDto, list which rule (if any) validates it today, and where (DTO validator, TaskItemValidator, EF constraint, or none).
  2. Identify any field where invalid input can reach the database, or cause a 500, or be accepted by one endpoint and rejected by another. Examples to try: over-long Title or Description, empty or whitespace-only Title on PATCH, BlockedReason length, DueDate extremes.
  3. Compare the three endpoints (POST, PUT, PATCH) for consistent behavior on the same invalid input.
Outcome
  • If every field is already covered by TaskItemValidator, close this issue with a short table of findings.
  • If there are gaps, add the missing rules (in Validators/, following the .InclusiveBetween / WithMessage pattern used for the offset) and tests under TaskFlow.Api.Tests/Validators/, or split out one issue per gap.
Context

Raised as a note-only observation by the implementation-validator during /feature-factory 207 (PR #363). Out of scope there.


Audit findings (2026-10-30, by reading the code; no requests were sent)

TaskItemValidator validates the entity after the DTO is mapped onto it. It runs on create (TaskItemsController.cs, around line 192) and in ApplyUpdateAsync (around line 437), so POST, PUT and PATCH are covered the same way. The three DTO validators cover only TimezoneOffsetMinutes, which is fine.

Field Validated by Gap
Title TaskItemValidator: not empty, max 100 none
Status, Priority TaskItemValidator: IsInEnum none
BlockedReason TaskItemValidator: max 200, only while Blocked none
Description nothing (no rule in TaskItemValidator, no length constraint in TaskDbContext) no length limit on POST, PUT or PATCH
DueDate nothing no range check; extremes are covered by #238
ParentTaskItemId, ProjectId controller checks not examined here

Comparable text fields are bounded: Project description 2000, Note content 2000, JournalNote content 2000, JournalLogEntry content 2000, JournalEntry summary 10000. Task Description is the only unbounded user text field.

Stored data check (2026-10-30, via the TaskFlow MCP list_tasks, 11 tasks, detailed format): the longest description is about 110 characters (task #68). Caveats: the MCP's target instance was not confirmed to be production, and the detailed format may truncate descriptions.

User Story

As a task owner, I want an over-long task description to be rejected with a clear message, so that task data stays bounded like every other text field and an accidental paste cannot bloat the database or the responses.

Acceptance Criteria

  1. Given a POST with a Description longer than 2000 characters, then the response is 400 with the message "Description must not exceed 2000 characters." and no task is created.
  2. Given a PUT with a Description longer than 2000 characters, then the response is 400 with the same message and the task is unchanged.
  3. Given a PATCH with a Description longer than 2000 characters, then the response is 400 with the same message and the task is unchanged.
  4. Given a Description of exactly 2000 characters, null, or empty, then POST, PUT and PATCH accept it.
  5. Given the rule, then it lives in TaskItemValidator (Validators/), not in the three DTO validators, so one rule covers every endpoint and the parent auto-complete path. Errors keep the BadRequest(validationResult.Errors) shape.
  6. Given the validator, then unit tests under TaskFlow.Api.Tests/Validators/ cover 2000 accepted, 2001 rejected, null and empty accepted, with the exact message. Controller tests cover PUT and PATCH rejection with the task unchanged.
  7. Given the finished change, then dotnet test and dotnet format --verify-no-changes pass, and backend line coverage stays at 75% or above.

Edge Cases

  • Existing data above the limit. PUT resends Description, so a stored description longer than 2000 characters would make every later update of that task fail until it is shortened. The MCP check found nothing near the limit, but it could not be confirmed against production. Check production data (for example with the MCP against the live instance) before merging.
  • Parent auto-complete. TryAutoCompleteParentAsync validates the parent entity (around line 575). A parent with an over-limit legacy description would fail validation there. Confirm what the user sees.
  • Counting. MaximumLength counts UTF-16 code units, not characters as the user perceives them. 2000 is the same unit the other validators use; do not change it.
  • Frontend. Check how the task forms surface a 400 on Description. Adding a maxLength attribute is optional and not required by this story.
  • The message and rule follow ProjectValidator (Description max 2000).

Out of Scope

  • DueDate range or wire format (#238).
  • Title, Status, Priority and BlockedReason rules (already covered).
  • Changing the TimezoneOffsetMinutes rules from #207.
  • Changing the error shape (BadRequest(validationResult.Errors) is the convention; see #364).
  • Global FluentValidation auto-validation.
  • Adding a database column constraint or migration.
  • ParentTaskItemId and ProjectId validation.
Dominant language
C#
Stars
1
Forks
0
Avg merge
2h 2m
Merged PRs (30d)
98

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from nevridge/TaskFlow

All issues in nevridge/TaskFlow

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.