Skip to content

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-empty childTaskIds
  • 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-empty blockedByTaskIds
  • Trigger: ALL blocking tasks have status: 'completed'
  • Action: Task transitions to status: 'to_do' with ready: 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":

  1. User creates N child tasks from the parent
  2. Parent status → exploded, childTaskIds populated
  3. Child tasks start with appropriate status (usually to_do)
  4. When ALL children reach completed, parent auto-transitions to completed

Block Mechanics

When a task is "blocked":

  1. User sets blockedByTaskIds to reference blocking tasks
  2. Task status → blocked
  3. When ALL blocking tasks reach completed, task auto-transitions to to_do with ready: 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:

  1. POST /api/tasks/{id}/stop triggers upsertTimeBillsForDay()
  2. Retrieves all completed time entries for that user/day
  3. Groups entries by (pillar, customer)
  4. Sums duration and rounds up to nearest quarter hour: Math.ceil(hours * 4) / 4
  5. 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):

  1. PATCH /api/time-bills/{id} sets status: 'approved'
  2. Lambda refreshes Xero tokens if needed
  3. Matches Cognito user email to Xero user
  4. Gets or creates Xero task for the pillar (using rate hierarchy)
  5. Creates Xero time entry with duration, date, user, and memo
  6. Updates bill: xeroTimeEntryId set, statussent_to_billing

Batch Operations

  • Batch approve: POST /api/time-bills/approve-all with array of bill IDs
  • Recompute: POST /api/time-bills/recompute recalculates all bills from time entries (admin only)