Status Workflows¶
Task Status State Machine¶
stateDiagram-v2
[*] --> intake : Created from Slack<br/>or external source
[*] --> to_do : Created manually
intake --> to_do : Triage / assign
to_do --> in_progress : Start work
in_progress --> completed : Finish
in_progress --> blocked : Waiting on deps
in_progress --> exploded : Split into subtasks
intake --> cancelled : Abandon
to_do --> cancelled : Abandon
in_progress --> cancelled : Abandon
exploded --> completed : All children completed<br/>(auto-transition)
blocked --> to_do : All blockers completed<br/>(auto-transition)
completed --> [*]
cancelled --> [*]
Status Definitions¶
| Status | Description |
|---|---|
intake |
New task, not yet triaged. Created from Slack or external sources. |
to_do |
Triaged and ready to work on. |
in_progress |
Actively being worked on (timer can be running). |
completed |
Work is done. |
cancelled |
Task was abandoned. |
exploded |
Task was split into child tasks. Parent waits for children. |
blocked |
Task is waiting on other tasks to complete before it can proceed. |
Automatic Transitions¶
The status-check Lambda runs on an EventBridge schedule and checks for tasks that should auto-transition:
Exploded → Completed¶
- Condition: Task has
status: 'exploded'and non-emptychildTaskIds - Trigger: ALL child tasks have
status: 'completed' - Action: Parent task transitions to
status: 'completed'
Blocked → To Do (Ready)¶
- Condition: Task has
status: 'blocked'and non-emptyblockedByTaskIds - Trigger: ALL blocking tasks have
status: 'completed' - Action: Task transitions to
status: 'to_do'withready: true
Frontend Redundancy
The web frontend also polls these conditions every 5 seconds via the useStatusTransitions hook, ensuring responsive UI updates without waiting for the next scheduled Lambda run.
Task Relationships¶
| Field | Type | Description |
|---|---|---|
parentTaskId |
String | Reference to parent task (set on child tasks when parent is exploded) |
childTaskIds |
String[] | IDs of child tasks created from exploding this task |
blockedByTaskIds |
String[] | IDs of tasks that must complete before this task unblocks |
Explode Mechanics¶
When a task is "exploded":
- User creates N child tasks from the parent
- Parent status →
exploded,childTaskIdspopulated - Child tasks start with appropriate status (usually
to_do) - When ALL children reach
completed, parent auto-transitions tocompleted
Block Mechanics¶
When a task is "blocked":
- User sets
blockedByTaskIdsto reference blocking tasks - Task status →
blocked - When ALL blocking tasks reach
completed, task auto-transitions toto_dowithready: true
TimeBill Status State Machine¶
stateDiagram-v2
[*] --> pending : Timer stopped<br/>(auto-created)
pending --> approved : Admin approves
pending --> rejected : Admin rejects
approved --> sent_to_billing : Xero sync succeeds<br/>(automatic)
sent_to_billing --> [*]
rejected --> [*]
Status Definitions¶
| Status | Description |
|---|---|
pending |
Auto-created from time entries, awaiting admin review. |
approved |
Approved by admin. Triggers Xero sync automatically. |
rejected |
Rejected by admin, with optional rejectionNote. |
sent_to_billing |
Successfully synced to Xero as a time entry. |
TimeBill Creation¶
Time bills are automatically generated when a timer is stopped:
POST /api/tasks/{id}/stoptriggersupsertTimeBillsForDay()- Retrieves all completed time entries for that user/day
- Groups entries by (pillar, customer)
- Sums duration and rounds up to nearest quarter hour:
Math.ceil(hours * 4) / 4 - Creates or updates TimeBill with
status: 'pending'
General Support Fallback
If a task has no pillarId, the system searches for a "General Support" pillar on the customer. If found, the time bill is categorized under it.
Approval → Xero Sync¶
When a bill is approved (manually or via batch approve):
PATCH /api/time-bills/{id}setsstatus: 'approved'- Lambda refreshes Xero tokens if needed
- Matches Cognito user email to Xero user
- Gets or creates Xero task for the pillar (using rate hierarchy)
- Creates Xero time entry with duration, date, user, and memo
- Updates bill:
xeroTimeEntryIdset,status→sent_to_billing
Batch Operations¶
- Batch approve:
POST /api/time-bills/approve-allwith array of bill IDs - Recompute:
POST /api/time-bills/recomputerecalculates all bills from time entries (admin only)