Velocity#
Velocity measures effort completed per iteration (sprint). It tracks how much work finishes each cycle to reveal trends and consistency.
Definition#
velocity = sum(effort) for items completed in the iteration
completion_rate = velocity / committed_effort * 100- Velocity is the total effort of items that are "done" within an iteration.
- Committed effort is the total effort of all items assigned to the iteration (done or not).
- Completion rate is the percentage of committed effort that was actually completed. If committed effort is 0, completion rate is omitted.
An item is "done" when:
- Issues (
velocity.unit: issues): Closed withstateReason: completed(not "not planned"). - PRs (
velocity.unit: prs): Merged.
Effort strategies#
Effort determines how much each work item contributes to velocity. Three strategies are available.
Count (effort.strategy: count)#
Every completed item counts as 1. Default; no extra configuration required.
velocity:
effort:
strategy: countVelocity becomes a simple count of completed items. All items are assessed.
Attribute (effort.strategy: attribute)#
Maps labels, issue types, or title patterns to effort values using matcher syntax. First match wins. Unmatched items are "not assessed" and contribute 0 effort.
velocity:
effort:
strategy: attribute
attribute:
- query: "label:size/L"
value: 5
- query: "label:size/M"
value: 3
- query: "label:size/S"
value: 1
- query: "type:Bug"
value: 2Matcher types:
label:<name>-- exact label matchtype:<name>-- GitHub Issue Type matchtitle:/<regex>/i-- title regex, case-insensitive
Unmatched items are "not assessed" and contribute 0 effort. When a high percentage are not assessed, the tool warns that velocity may be understated.
Numeric (effort.strategy: numeric)#
Reads effort from a Number field on a GitHub Projects v2 board.
velocity:
effort:
strategy: numeric
numeric:
project_field: "Story Points"Requires project.url. Items without a value (null) are not assessed. Negative values are treated as not assessed with a warning.
Iteration strategies#
Iterations define the time boundaries for grouping work. Two strategies are available.
Project field (iteration.strategy: project-field)#
Reads iteration boundaries from a ProjectV2 Iteration field. Each iteration has a name, start date, and duration defined in the board settings.
velocity:
iteration:
strategy: project-field
project_field: "Sprint"
count: 6Requires project.url. Fetches both active and completed iterations, then matches items by their iteration field assignment.
Fixed (iteration.strategy: fixed)#
Computes iteration boundaries using calendar math from an anchor date and a fixed length. No project board required.
velocity:
iteration:
strategy: fixed
fixed:
length: "14d" # 14-day sprints
anchor: "2026-01-06" # a known sprint start date
count: 6Iteration windows are generated by stepping forward/backward from the anchor date in increments of length. Items are matched by their close/merge date falling within the window.
Supported length formats: Nd (days, e.g., 14d) or Nw (weeks, e.g., 2w).
How fixed iterations find items#
Without a project board, the tool uses the GitHub Search API to find items closed/merged within each window:
- Issues are found with
is:issue is:closed reason:completed closed:<start>..<end> - PRs are found with
is:pr is:merged merged:<start>..<end> - The
scope.queryfilter is applied to narrow results
Not-assessed items#
With attribute or numeric effort strategies, items that match no rule or lack a field value are "not assessed." These items:
- Contribute 0 effort to velocity
- Are still counted in
items_total - Are reported separately in
not_assessedcount - Are listed by number when
--verboseis used
Insights fire when not-assessed items are prevalent:
- 100% not assessed: "All N items lack effort estimates -- velocity will be 0 until estimates are added."
- 50%+ not assessed: "X% of items (N/M) lack effort estimates -- velocity may be understated."
Trend indicators#
Each iteration in the history shows a trend compared to the previous iteration:
- up arrow: Velocity increased
- down arrow: Velocity decreased
- dash: Velocity unchanged
Aggregate statistics#
Across all historical iterations:
- Average velocity: Mean effort completed per iteration
- Average completion: Mean completion rate
- Standard deviation: Variability in velocity (requires 2+ iterations)
- Coefficient of variation (CV): StdDev / Mean -- when CV > 0.5, the tool warns about inconsistent sprint commitments
Configuration reference#
| Config field | Type | Default | Description |
|---|---|---|---|
velocity.unit | string | "issues" | Work item type: "issues" or "prs" |
velocity.effort.strategy | string | "count" | How effort is assigned: "count", "attribute", or "numeric" |
velocity.effort.attribute | list | -- | Matcher rules for attribute strategy (required when strategy is attribute) |
velocity.effort.attribute[].query | string | -- | Matcher pattern (e.g., "label:size/L") |
velocity.effort.attribute[].value | number | -- | Effort value for matching items (must be >= 0) |
velocity.effort.numeric.project_field | string | -- | Project board Number field name (required when strategy is numeric) |
velocity.iteration.strategy | string | -- | How iterations are bounded: "project-field" or "fixed" |
velocity.iteration.project_field | string | -- | Iteration field name on the board (required for project-field) |
velocity.iteration.fixed.length | string | -- | Iteration length, e.g., "14d" or "2w" (required for fixed) |
velocity.iteration.fixed.anchor | string | -- | A known iteration start date, YYYY-MM-DD (required for fixed) |
velocity.iteration.count | int | 6 | Number of past iterations to display |
Example output#
Pretty format#
Velocity dvhthomas/gh-velocity
Unit: issues Effort: count Iteration: fixed (14d)
Current: Mar 4 – Mar 17 (day 11/14)
Velocity: 4 items (of 6 committed) — 66.7% complete
History
Iteration Velocity Committed Done/Total Completion Trend
Feb 18 – Mar 3 7 items 8 items 7/8 87.5% ▲
Feb 4 – Feb 17 5 items 7 items 5/7 71.4% ▼
Jan 21 – Feb 3 6 items 6 items 6/6 100.0% ─
Average velocity: 6.0 items/iteration (std dev: 1.0)
Average completion: 86.3%JSON format#
{
"repository": "dvhthomas/gh-velocity",
"unit": "issues",
"effort_unit": "items",
"current": {
"name": "Mar 4 – Mar 17",
"start": "2026-03-04T00:00:00Z",
"end": "2026-03-18T00:00:00Z",
"velocity": 4,
"committed": 6,
"completion_pct": 66.7,
"items_done": 4,
"items_total": 6,
"not_assessed": 0,
"day_of_cycle": 11,
"total_days": 14
},
"history": [ ... ],
"avg_velocity": 6.0,
"avg_completion": 86.3,
"std_dev": 1.0
}Commands#
gh velocity flow velocity-- velocity report (default: current + last 6 iterations)gh velocity flow velocity --current-- current iteration onlygh velocity flow velocity --history-- past iterations onlygh velocity flow velocity --iterations 3-- override iteration countgh velocity flow velocity --verbose-- show not-assessed item numbers
Insights#
| Insight | When it fires | What it means |
|---|---|---|
| Not assessed | Items lack effort estimates | Some or all items have no effort value. With attribute strategy, check your matchers. With numeric strategy, check that the project field is populated. |
| High completion | Current iteration is 100% complete | All committed work is done — the team may have capacity for more. |
| Zero velocity | Zero effort completed across all iterations | Either the effort strategy is misconfigured, or no work was completed. Run config validate --velocity to diagnose. |
| High variability | Velocity CV > 1.0 across iterations | Sprint commitments vary significantly between iterations. Consider stabilizing scope or adjusting iteration length. |
See also#
- Setting Up Flow Velocity -- effort and iteration strategy guide
- Throughput -- simpler unweighted count metric
- Configuration Reference: velocity -- all config fields