Teams and identities
Every per-person panel rests on one small map in ~/.whodunit/config.json โ
which addresses belong to the same person โ and every per-team panel on
DevLake's team tables. Neither is collected; both are typed, and this page
is how, including what the config teams map does and does not reach today.
Identities โ one person, several addressesโ
A single developer picks up a second identity the moment one machine is configured with a GitHub noreply address and another with their real one. Nothing announces it: both are valid, both commit, and the dashboards show two contributors. Measured on one install, 153 of 985 commits sat under a second address, so filtering to either identity silently dropped a sixth of the work.
{
"identities": {
"12345+alice@users.noreply.github.com": "alice@example.com",
"alice@laptop.local": "alice@example.com"
}
}
Keyed by the address as git wrote it, valued by the canonical address. The literal committer email stays in the journal, the sidecar and every commit object; the map resolves at read time, so a wrong entry is fixed by editing one line rather than by re-syncing history.
An address with no entry is its own identity. Absent never means
merged. dun will not merge two identities on its own โ merging is a claim
about who somebody is โ so:
dun identities
lists every committer email found in your instrumented repositories, flags addresses that look like the same person, and prints the configuration lines. It never applies them. The decision stays with you.
Teams โ who is on which teamโ
{
"teams": {
"platform": ["alice@example.com", "bob@example.com"],
"growth": ["carol@example.com"]
}
}
Keyed by team, valued by canonical addresses. Keyed this way because that is the shape someone maintains by hand: a team gains a member far more often than a person changes team, and this form makes the common edit one line in one place. Aliases resolve first, so a person's second machine lands in the same team as their first.
This is organisation metadata, not new personal data. The addresses are already in every commit object; which team someone is on is a fact about the org chart and adds no observation of a person. See Privacy.
Where the dashboards get teams from todayโ
The dashboards read DevLake's teams and team_users tables, not the
config map. Every team filter joins team_users.user_id against the
committer email, after aliases resolve. Those tables are filled through
DevLake's org plugin โ two CSVs, teams.csv and users.csv, with the
committer email as each user's Id โ as described in
Setting up the datalake.
The teams map in config.json is defined and validated, but nothing
publishes or reads it yet โ not dun sync, not the dashboards. Until it
is wired, populate DevLake's tables directly. The intended precedence when
it lands โ a value typed in config wins over one that arrived on a
connector sync โ is why the map exists, and this page will say so when it
is true.
A contributor in no team is (unassigned), never dropped. The option is
always offered in every team dropdown, even when nobody is currently
unassigned โ someone will commit from an unmapped address, and a filter
built from current membership would hide them the moment they appear. A
person vanishing from a filter is the failure this whole area exists to
prevent.
Editing the fileโ
dun config set handles scalar settings. The two maps are edited in the
file directly:
$EDITOR ~/.whodunit/config.json
Nothing needs re-syncing afterwards: both maps resolve when a dashboard runs its query, against the addresses already in the lake.
Where it showsโ
Every dashboard with a Team or Contributor filter; Team Adoption and its rollout view; the per-team and per-person rows on the Delivery Forecast; the funnel per team. The DevLake deployment also reads the same maps when it seeds team tables for a demo database.