CodeHerderSearch⌘KRequest access →

Reading the task flow report

See where a task's elapsed time goes between arrival and completion, and why ready work isn't running.

Understanding costs answers “what did this cost?” Observability answers “where did the time go inside a session?” Task flow answers a third question: where does a task’s elapsed time go between arrival and completion, and why isn’t ready work running?

Where to find it

Today you read the workspace-wide report from the command line. The commands below — three ch observability flow subcommands, plus ch task flow for one task’s own swimlane — print every figure this page describes, for a workspace or a group.

One piece of it is in the app already: a task’s own page carries a Lifecycle flow panel, covered in One task’s own flow.

The workspace report also has a page in the app, with the panels described below. Nothing links to it yet, so don’t go hunting for it in the sidebar or on the Observability page — use the commands instead. The panel descriptions still tell you what each figure means, because the command line prints the same ones.

Like the tool-time report, task flow covers a workspace, or a group — a group’s report covers every workspace nested beneath it.

The summary

Five figures cover the window. ch observability flow prints them, and they head the report’s page as a row of stat tiles:

  • Arrivals — how many tasks were created in the window.
  • Completions — how many tasks finished in the window.
  • WIP — work in progress: how many of this window’s tasks are still open. It asks whether the task is open now, not whether it was open when the window closed.
  • Completion latency — how long a task that finished in the window took from arrival to finishing, p50 with p95 alongside it.
  • Queue age — how long the work still waiting has been waiting, p50 with p95 alongside it. It is a snapshot at the end of the window. Only tasks queued at that instant count. Work that already started counts for nothing. Read a rising p95 as “the backlog is ageing”, not “tasks took longer to start”.

The three charts

Below the summary, three charts read the same per-bucket data:

  • Cumulative flow — measured wait-state occupancy per time bucket, stacked. Select a band to open the task cohort behind it.
  • Arrivals and completions — how much work entered and left the system in each time bucket.
  • Queue age — how long the work waiting at each time bucket had already waited.

Selecting a band on any of the three opens a Selected task cohort list below them — the tasks behind that bucket (and that wait state, if you picked one band of a stacked bar). Each row shows the task, its type, priority, current stage, wait state, and when it was created. Read the wait state as the task’s state in the bucket you selected — the state it spent most of that bucket in, or the band you picked. Only stage is the task’s stage right now. Clear the selection to close the cohort and go back to the whole window.

Wait states

Every instant of a task’s life, from arrival to completion (or to now, if it’s still open), falls into one of six wait states:

  • Blocked explicit — someone filed a blocker on the task directly.
  • Blocked dependency — the task is waiting on an unfinished upstream task.
  • Executing — a session is actively working the task.
  • Queued — the task is ready to run, but no session has picked it up yet.
  • Awaiting handoff — the task is at a stage that doesn’t run an agent — a human review, or a container stage waiting on its children.
  • Unknown — no evidence survives for that stretch of time.

The Wait-state occupancy table below the charts gives one row per state, with the total time this window’s tasks spent in it. The second column is a duration, not a task count. From the command line, the same six values are spelled blocked_explicit, blocked_dependency, executing, queued, awaiting_handoff, and unknown.

Why work did not run

The Why work did not run table lists the placement and capacity refusals CodeHerder recorded in the window — a reason, how many times it was recorded, and a plain-language explanation of what it means. These events age out over time, so an empty table doesn’t prove nothing was ever refused, only that nothing survives in this window.

See The Placement report for the full picture of device eligibility behind a single task, and Why isn’t my task moving? if you’re trying to unstick one task right now — task flow is the aggregate, workspace-wide view of the same refusals.

One task’s own flow

A task’s own page has a Lifecycle flow panel: a lane per stage attempt, showing how that attempt’s own time split across wait states. From the command line:

ch task flow <taskId>

This prints the same swimlane as a table — one row per stage attempt, with its wait state, duration, session count, and start time. Omit <taskId> and it defaults to CH_TASK_ID.

From the command line

ch observability gives you the same report as three windowed commands, plus the per-task one above:

ch observability flow

Prints the summary above — arrivals, completions, WIP, completion latency, queue age, wait-state occupancy, and the refusal-reasons table — for the last 7 days by default.

ch observability flow buckets --window month

Prints one row per time bucket: arrivals, completions, WIP, and queue-age percentiles.

ch observability flow tasks --state queued

Prints the cohort task list. Narrow it with --state (one of the six wait states above) or --bucket (an RFC 3339 bucket start, copied straight from flow buckets’ own output). It pages like any other ch list: --limit and --cursor.

--bucket also decides what the wait-state column means. Pass one and you get the task’s state in that bucket, the same as the Selected task cohort list. Leave it off and you get the task’s state as of the end of the window — its state now, for a window ending today.

All three take an optional workspace or group as their first argument, defaulting to your current workspace, and the same window flags ch costs uses:

ch observability flow my-group
ch observability flow buckets --since 30d

See Understanding costs for how --window and --since work.

Who can see it

Any workspace member can read the task flow report, in the app or from the CLI — there’s no separate permission to grant.

Last updated

CodeHerder

Round up your herd.

Bring every human and every agent onto one table. Watch the work move. Costs update as it happens.

Try "pricing", "connect a device", or "who reviews the code"

↑↓ move · ↵ open · esc close