Where each number comes from
Two datasets sit in one database. dun publishes what it recorded on the
developer's machine โ commits, agent events, sessions, identities, teams.
DevLake collects the rest from the systems you connect โ the git host, the
tracker, CI, incidents. The dashboards join them. This page is the join
map: for each thing DevLake can supply, which tables it fills, which
columns the panels read, the key on which it meets dun's data, which
dashboards depend on it, and what those dashboards show when it is
missing โ because a missing connection never renders as an error, it
renders as a plausible-looking absence.
Every table and column below was read out of the dashboard JSON in the repository, so it can be checked.
The join mapโ
dun publishes DevLake collects
โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
whodunit_commits.commit_sha โโโโโโฌโโโโ issue_commits.commit_sha โโ issues โโ board_issues โโ boards
โ (tracker: Linear, Jira, Azure Boards)
โโโโโ repo_commits.commit_sha โโ repos.name
(git host: GitHub, GitLab, Bitbucket, Azure DevOps)
committer email โ whodunit_identities โฌโ whodunit_teams.contributor (dun: config map, read first)
โโ team_users.user_id โโ teams (DevLake: org plugin CSV, fallback)
whodunit_sessions.model โโโโโโโโโโโโโ whodunit_model_prices.model (fetch-model-prices.py)
time window only (no join to dun rows):
cicd_deployment_commits โโ project_mapping (CI: GitHub Actions, GitLab CI, Jenkins, CircleCI โฆ)
project_pr_metrics (computed by DevLake from pull requests)
incidents (PagerDuty, Opsgenie, incident.io, webhook)
Three things the diagram makes visible. The tracker and the git host join
to dun's commits by commit SHA, which is why every attribution can be
tied to an issue and a repository name without dun ever storing either.
Teams join by committer email, so any team source has to speak in
emails. And the delivery metrics โ deployments, pull-request timings,
incidents โ never join to dun rows at all: they sit beside the adoption
curve in time, which is why every "does delivery change with adoption"
panel is a correlation over weeks and says so.
Git host โ GitHub, GitLab, Bitbucket, Azure DevOpsโ
| DevLake table | columns the panels read | joined on |
|---|---|---|
repo_commits | commit_sha, repo_id | = whodunit_commits.commit_sha |
repos | id, name | repo_commits.repo_id = repos.id |
commits | sha, authored_date | = whodunit_commits.commit_sha |
What it supplies. The repository's name. dun identifies a
repository by its root-commit id and, by design, never records the name or
the remote โ see Privacy. The name only ever
appears on a dashboard by joining through DevLake's repo_commits, and
the $repo dropdown and every per-repository panel resolve it that way
with COALESCE(MAX(repos.name), LEFT(repo_id, 8)). commits also
supplies the denominator for Coverage: how many of the repository's
commits carry a trailer at all, which dun's own table cannot answer
because every row in it exists because a trailer was written.
Depends on it. Attribution (Coverage), Adoption and its rollout view
(per-repository adoption and the not-instrumented share), the $repo
filter on the Executive Summary.
Without it. Repositories appear as eight-character ids rather than names. Coverage cannot be computed and the panel says so. Everything else still works โ attribution never needed the name.
Setup. Connecting your data: the GitHub token
needs read:user for the commit-to-author mapping.
Tracker โ Linear, Jira, Azure Boardsโ
| DevLake table | columns the panels read | joined on |
|---|---|---|
issues | id, issue_key, status, created_date, resolution_date, lead_time_minutes, assignee_name | via issue_commits |
issue_commits | issue_id, commit_sha | commit_sha = whodunit_commits.commit_sha |
board_issues | board_id, issue_id | scopes an issue to the $board you selected |
boards | id, name | the $board dropdown |
What it supplies. Everything about tasks: cycle time, issues opened and closed, whether an issue was AI-assisted (any linked commit is), the backlog and its drain rate, productivity per task, and the assignee that the per-team forecast uses for open work.
The one rule that decides whether any of it appears: the issue key has
to be in the commit message. DevLake fills issue_commits by matching
keys like WHO-123 in commit messages. A team that does not write keys
into commits has issues in issues and nothing in issue_commits, and
every assisted-issue figure reads zero โ not because nothing was
delivered, but because nothing was linked. All 32 panels that join the
two carry the same guard, issue_key REGEXP '^[A-Z][A-Z0-9]+-[0-9]+$',
because a tracker with bare-integer keys would otherwise attribute
unrelated commits and report AI-assisted issues that do not exist.
Depends on it. The Executive Summary's cycle-time comparison and issue panels, the Delivery Forecast entirely, the Productivity page's cycle time per stratum, the Investment Case's cost per assisted issue, the funnel's last stage, the Leadership view, Adoption's issue cohorts.
Without it. Every issue panel is empty or reads zero, and the cohort
panels withhold rather than compare: the median-cycle panels need five
issues a side and show n/a below that. The tooltip on each says
"unlinked, not undelivered". Commit-grain panels are unaffected.
Which tracker. Linear and Jira are both read; the $board dropdown
lists linear: and jira: boards and deliberately excludes github:
ones, whose repositories also appear in the boards table. Migrating from
one tracker to the other keeps both sets of issues visible; keys are
matched per board.
CI โ GitHub Actions, GitLab CI, Jenkins, CircleCI, and othersโ
| DevLake table | columns the panels read | joined on |
|---|---|---|
cicd_deployment_commits | cicd_deployment_id, cicd_scope_id, environment, result, finished_date | time window; cicd_scope_id = project_mapping.row_id |
project_mapping | row_id | scopes deployments to your project |
What it supplies. Deployment frequency, change failure rate
(result = 'FAILURE'), and the deployment outcomes chart. Not joined to
dun rows: a deployment carries commits, but the panels read it as a
weekly count beside the adoption share.
Depends on it. AI Impact on Delivery, the board-facing delivery view.
Without it. The deployment panels are empty and the DORA benchmark tiles say "no data in range". Adoption and attribution are unaffected.
Setup. A CI run only counts as a deployment when it matches the deployment pattern configured on the DevLake project โ see Deployments need a pattern.
Pull requests โ computed by DevLakeโ
| DevLake table | columns the panels read | joined on |
|---|---|---|
project_pr_metrics | pr_cycle_time, pr_merged_date, and on the board view pr_coding_time, pr_pickup_time, pr_review_time, pr_deploy_time | time window |
What it supplies. Lead time for changes and its stages. DevLake
derives this table from the git host's pull requests; it needs the git
host connected and the project to include the repository. The dashboards
read this table rather than pull_requests directly, so the two delivery
pages agree.
Depends on it. AI Impact on Delivery, the board-facing delivery view.
Without it. Lead-time panels empty; "PRs measured" reads zero. A stage with no data โ review stages need review activity collected โ is absent from the stacked chart, not zero.
Incidents โ PagerDuty, Opsgenie, incident.io, or a webhookโ
| DevLake table | columns the panels read | joined on |
|---|---|---|
incidents | created_date, resolution_date | time window |
What it supplies. Mean time to recovery. Thin by nature โ a handful of rows โ and the panels say it moves a lot with each one.
Depends on it. AI Impact on Delivery, the board-facing delivery view.
Without it. MTTR reads "no data in range". Nothing else is affected.
Setup. Mean time to recovery needs incidents.
Teams โ two sources, config firstโ
| table | columns the panels read | joined on |
|---|---|---|
whodunit_teams (dun) | contributor, team_name | contributor = committer email, after aliases |
team_users, teams (DevLake) | user_id, team_id, name | user_id = committer email, after aliases |
What it supplies. Every team filter and every per-team panel โ 75
panels across ten dashboards โ resolve
COALESCE(whodunit_teams.team_name, teams.name, '(unassigned)'): the
config map published by dun sync first, DevLake's org plugin second,
(unassigned) always offered. Both routes, and why DevLake's user Id
must be the committer email, are in
Teams and identities.
Without either. Everyone is (unassigned), every panel still
renders, and the team dropdown offers the one option. Nothing errors and
nobody disappears.
Identities โ dunโ
| table | columns the panels read | joined on |
|---|---|---|
whodunit_identities | alias, canonical | alias = contributor on commits, events and sessions |
Not a DevLake source, but it sits in front of every other join: 146
panels resolve COALESCE(canonical, contributor) before filtering by
person or looking up a team, so one person's several addresses count as
one. With no aliases configured the join is inert and every result is
byte-identical to the plain form.
Model prices โ a script, not a connectorโ
| table | columns the panels read | joined on |
|---|---|---|
whodunit_model_prices | model, provider, input_usd, cache_read_usd, cache_write_usd, output_usd, source, fetched_at | model = whodunit_sessions.model |
Filled by fetch-model-prices.py from the providers' pricing pages;
created empty by dun's schema so the join runs before anyone has
fetched. A session whose model has no row is reported as unpriced, never
priced at zero. See The investment case.
Reading an empty panelโ
Work down the map. A panel about issues that reads zero is almost always
the issue key missing from commit messages, not a missing connector. A
repository showing as an id is the git host not connected. DORA tiles
saying "no data" are the CI pattern or the incident source. Everyone in
(unassigned) is teams not yet loaded by either route. In every case the
attribution underneath โ which commits an agent wrote, and how much โ is
already there; the connection adds context to it, and its absence removes
only that context.