Status: Planned next feature. This document describes the next implementation slice; the current codebase already supports task intake, but does not yet enforce lifecycle transitions.
Source PRD: docs/prd/task-lifecycle-and-status-rules.md
Extend the existing task intake flow with one meaningful workflow feature set:
- constrained status transitions
- blocked-task reasoning
- read-only handling for completed work
This spec is intended to drive backend, frontend, and e2e implementation together on top of the existing create flow.
GET /api/tasks
- Returns the current task list ordered by creation time or ID
POST /api/tasks
- Already exists from the business-task intake slice
- Continues to create a new task with initial status
TODO - Request and response shape should stay aligned with
docs/spec/business-task-intake-and-planning.md - Lifecycle work should extend that existing contract rather than replace it
- Response should extend the current task shape with lifecycle-specific fields when needed, such as
blockedReasonandupdatedAt
{
"id": 12,
"title": "Prepare release notes",
"status": "TODO",
"customerRequest": "Customer needs a visible delivery task",
"requestedWork": "Prepare business task intake flow",
"targetDeliveryDate": "2026-04-10",
"buildEstimate": "3 engineering days",
"owner": "Sky",
"blockedReason": null,
"createdAt": "2026-03-21T08:00:00Z",
"updatedAt": "2026-03-21T08:00:00Z"
}PATCH /api/tasks/{id}
- Updates status and, when needed,
blockedReason - Request body:
{
"status": "BLOCKED",
"blockedReason": "Waiting for API schema review"
}Validation and transition failures should return a machine-readable response:
{
"code": "INVALID_STATUS_TRANSITION",
"message": "Task cannot move from TODO to DONE",
"fieldErrors": {
"status": "Allowed next statuses: IN_PROGRESS, BLOCKED"
}
}- The existing business-task intake fields remain required as defined in
docs/spec/business-task-intake-and-planning.md blockedReasonis optional except when status isBLOCKEDblockedReasonlength: 10 to 200 characters after trimming when provided
TODOIN_PROGRESSBLOCKEDREADYDONE
| From | To |
|---|---|
TODO |
IN_PROGRESS, BLOCKED |
IN_PROGRESS |
TODO, BLOCKED, READY |
BLOCKED |
TODO, IN_PROGRESS |
READY |
IN_PROGRESS, BLOCKED, DONE |
DONE |
none |
- Moving to
BLOCKEDrequiresblockedReason - Moving away from
BLOCKEDclearsblockedReason - Moving to
DONEfrom anything other thanREADYmust fail - A
DONEtask cannot be updated through the normal patch endpoint
Existing sample_task data is not enough for this feature.
Expected schema changes:
- add
blocked_reasonnullable column - add
updated_attimestamp column - ensure seeded rows can coexist with the new fields
Liquibase should own the schema change so local Docker and k3d deployments stay aligned.
The task panel should support:
- create task input
- visible status badges
- status change control
- blocked reason input when moving to
BLOCKED
- initial loading
- optimistic submit disabled state
- successful create/update refresh
- field-level validation error
- rejected transition error
- read-only treatment for
DONE
- Invalid transitions should explain the rule, not just say “failed”
BLOCKEDtasks should display the blocking reason inlineDONEtasks should indicate they are closed and not editable
The e2e suite should prove at least these scenarios:
- Create a new task and verify it appears with status
TODO - Move
TODO -> IN_PROGRESS -> READY -> DONE - Attempt
TODO -> DONEand verify the request is rejected with a visible error - Attempt to move a task to
BLOCKEDwithout a reason and verify validation feedback - Move a task to
BLOCKEDwith a valid reason and verify the reason is rendered in the UI
This feature is still intentionally small.
It should not introduce:
- authentication
- background jobs
- multi-user conflict handling
- pagination
- audit trails
The purpose is to demonstrate one level of meaningful product complexity, not to build a full task management product.