Engineering metrics caveats — generated by the render, not written by a person
Every dashboard has a page of disclaimers that nobody reads. This one is assembled from what the render actually did to this window, so a caveat that does not apply is absent rather than greyed out — which is the only thing that keeps the ones present worth reading.
| Key | Caveat |
|---|---|
| Window | 2026-04-01 to 2026-06-30. |
| Two grains | The delivery funnel and the shipped ratio count 37 FEATURES. Every other figure counts 122 issues. The two are not meant to match. |
| Systems | Jira and Azure DevOps — 122 tasks after de-duplication. |
| De-duplication | 14 tasks existed in both systems and were merged on a normalised title: keys stripped, brackets removed, punctuation collapsed. |
| Late joiner | Engineer C: first recorded ticket movement 2026-05-14. Every rate for this person runs from that date, not the full window. |
| Unclassified | 16 tasks carry no classification. They are counted as their own class rather than assumed into one of the others. |
| Attention days | A share-of-attention proxy, not effort. Tasks overlap. |
A window's own caveats. A different window produces a different list.
Written by the render, not by a person. A caveat that does not apply to this window is absent rather than greyed out.
What it measures, and where the data comes from
It measures the render. Each entry is emitted because a specific condition held while the widgets on this page were computed — a task existed in both trackers, a person's first activity fell inside the window, a board state was unrecognised.
Two entries are unconditional because they are true of the method rather than of the data: attention-days are a proxy, and lines-changed is unavailable. Dropping either would let someone quote a number the view cannot support. (Window and Systems also appear on every render, but they describe what this render covered rather than a limit on what it can support — so four entries are always present, and two of those four are caveats in the strict sense.)
The caveats, and what each one is protecting you from
| Key | Emitted when | What it prevents |
|---|---|---|
| Window | always | Quoting a figure without the period it covers. |
| Commit window | commit data stops before the window ends | Reading a data boundary as inactivity — the two are indistinguishable in a chart. |
| Two grains | the page is showing both features and issues | Reading two deliberately different totals as a bug. See below. |
| Cancelled before starting | any task was cancelled with no work recorded | An exclusion nobody can see, which is indistinguishable from a defect. |
| Systems | always | Not knowing which trackers the count spans. |
| Both systems | one system alone would have found fewer tasks | Under-counting a window that spans a migration. |
| De-duplication | tasks were merged across systems | Assuming a task in two trackers was counted twice. |
| Task age | a migration cutoff is configured | Treating an importer's changelog burst as work. |
| Identity | logins were resolved to fewer people | Trusting a per-person split without knowing identities were merged — and the entry says the view cannot show you which merges it made. |
| Late joiner | a person's first activity falls inside the window | Reading a start date as idleness. One entry per person, naming the date. |
| "Approved" | the board has an Approved state | Guessing whether that state means groomed or completed — it reports what transitions out of it actually did. |
| Unmapped states | a raw state is not in the stage map | A delivery funnel whose middle bars are empty for a configuration reason. |
| Unclassified | any task carries no classification | Assuming unclassified work was quietly folded into another class. |
| Attention days | always | Converting a share-of-attention proxy into effort or FTE. |
| Not measured | always | Quoting lines changed or pull-request size from this view. Azure DevOps reports zero additions and deletions on nearly all pull requests and counts files rather than lines, so both are omitted rather than estimated. |
That is the complete set — fifteen possible entries, of which four appear on every render (Window, Systems, Attention days and Not measured) and the other eleven appear only when they apply.
The one that matters most: two grains
Two totals on one page that are not meant to match is the single thing readers most often report as a bug, so it gets the longest entry:
That last clause is the part that surprises people. A feature can appear in the window because one sub-task moved, and be placed in the funnel using children that were never in the window at all. That is correct — a feature's stage is a fact about the feature, not about the window — and it is why the funnel's shape can change without any in-window activity explaining it.
How to read it
Read it before quoting anything from the page, and read it as a list of things that are true rather than a list of apologies. Most entries are statements about coverage — which systems, how many tasks, whose rates start when.
Treat an Unmapped states entry as blocking. It means the delivery funnel's shape is a configuration artefact, and the entry names both the states and the fix.
Treat an Identity entry as a caution on every per-person widget, and note its unusual honesty: it tells you merges happened and that it cannot show you which ones. A wrong merge is as damaging as a missing one.
Note the limitation the late-joiner entry declares about itself: commits are not yet counted in the first-activity date, so someone who was committing before their first ticket movement gets a window that is too short and a rate that is flattered.
What it tells you over time
A caveat list that gets shorter across windows is a board being cleaned up: states mapped, epics marked, classifications filled in. That is the trend to drive, and it is the closest thing to a data-quality score the view offers.
A new entry appearing is worth investigating on its own. An Unmapped states entry that was not there last window means someone added a workflow state; an Identity entry that appears means logins started resolving differently.
For this widget: the caveats describe the population in aggregate — the ledger is that same population row by row. When a caveat names a count (14 merged, 16 unclassified), the corresponding filter in the ledger shows you exactly which tasks it means.
The raw state sits next to the mapped stage deliberately: that is what lets you see how a state was interpreted and argue with it, rather than being handed a stage and asked to trust it. See the ledger reference for the full column list, or Ledger & caveats for the method behind it.
Example situations
1. Circulating the view outside the team
What you're seeing: a quarter's Team Focus view that you are about to put in a board pack. The numbers are good. The caveats say 31% of tasks were off-board, 16 are unclassified, and one person's rates run from six weeks into the window.
How to react: send the caveats with it. Every one of those three facts is something a reader would otherwise assume the opposite of, and each is the kind of thing that destroys trust in a dashboard when discovered later rather than disclosed upfront.
2. Two widgets that appear to contradict each other
| Widget | Total | Grain |
|---|---|---|
| Where the Work Ended Up | 37 | features |
| Landed in Production | 37 | features |
| How Work Was Classified | 122 | issues |
| Never Moved | 122 | issues |
Two totals on one page, neither wrong.
What you're seeing: 37 in two widgets and 122 in two others, on one screen. It looks like a filtering bug and it is not — it is two grains, reported as such. Three of the four widgets say which grain they are on their own face, and this widget prints both numbers together.
How to react: check the Two grains caveat before raising it as a defect. If it is present, the discrepancy is explained and expected.
Frequently asked
- What are the limitations of these engineering metrics?
- Method & Caveats lists them for the specific window you are looking at, generated by the render rather than written in advance. Two entries are unconditional because they are true of the method: attention-days are a proxy rather than effort, and lines-changed is unavailable.
- Why does the caveat list change between windows?
- A caveat that does not apply is absent rather than greyed out. A fixed list of disclaimers gets skimmed and stops being read; a list where every entry corresponds to something that happened to this window keeps its weight.
- Why do two widgets on the page show totals that do not match?
- Because they count different things, and the Two grains caveat says so explicitly. The delivery funnel and the shipped ratio count features; every other figure counts issues. It is the discrepancy readers most often report as a bug.
- What does the commit-window caveat mean?
- That commit data stops before the window ends, so commit and active-day figures are bounded there. It is a data boundary, not inactivity — a distinction the widget makes explicitly because the two look identical in a chart.
Related widgets
- Every Task, and Why — the same population, row by row.
- Where the Work Ended Up — the feature-grain widget the Two grains caveat is about.
- Board Elements Worked On — the off-board gap, counted directly.
- Where the Attention Went — where the late-joiner caveat is applied.
- Method: Ledger & caveats.
- Back to the widget reference.
Last updated