Skip to main content
Version: Next

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 tablecolumns the panels readjoined on
repo_commitscommit_sha, repo_id= whodunit_commits.commit_sha
reposid, namerepo_commits.repo_id = repos.id
commitssha, 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 tablecolumns the panels readjoined on
issuesid, issue_key, status, created_date, resolution_date, lead_time_minutes, assignee_namevia issue_commits
issue_commitsissue_id, commit_shacommit_sha = whodunit_commits.commit_sha
board_issuesboard_id, issue_idscopes an issue to the $board you selected
boardsid, namethe $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 tablecolumns the panels readjoined on
cicd_deployment_commitscicd_deployment_id, cicd_scope_id, environment, result, finished_datetime window; cicd_scope_id = project_mapping.row_id
project_mappingrow_idscopes 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 tablecolumns the panels readjoined on
project_pr_metricspr_cycle_time, pr_merged_date, and on the board view pr_coding_time, pr_pickup_time, pr_review_time, pr_deploy_timetime 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 tablecolumns the panels readjoined on
incidentscreated_date, resolution_datetime 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โ€‹

tablecolumns the panels readjoined on
whodunit_teams (dun)contributor, team_namecontributor = committer email, after aliases
team_users, teams (DevLake)user_id, team_id, nameuser_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โ€‹

tablecolumns the panels readjoined on
whodunit_identitiesalias, canonicalalias = 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โ€‹

tablecolumns the panels readjoined on
whodunit_model_pricesmodel, provider, input_usd, cache_read_usd, cache_write_usd, output_usd, source, fetched_atmodel = 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.