From 102676d029ca7686797d4c1339a053d125cd54fc Mon Sep 17 00:00:00 2001 From: Rodrigo Matos Date: Thu, 27 Aug 2026 07:18:28 -0300 Subject: [PATCH 1/2] fix(platform): only show Hyper Engineers with a resolved GitHub username MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even after grouping by identity (#202), some people still can't be merged automatically — GitHub's commits-by-author-email API (iris/cli.py's _resolve_emails_via_repo) only resolves an email to a login when that email is linked and verified on the person's GitHub account. When it isn't, that identity falls back to its email's local part as a key, which won't match any other alias for the same person — so they can still show up as a second, unlinked entry alongside their properly-resolved one. Rather than show a name with no way to confirm identity or link to a profile, computeHyperEngineers now omits engineers with no resolved github. It's also the entries most likely to be an unresolved duplicate in the first place, since github is the strongest identity signal grouping already prefers. Co-Authored-By: claude-code_2-1-238_agent --- platform/lib/queries/org-summary.ts | 48 +++++++++++++++++------------ platform/tests/org-summary.test.ts | 45 ++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/platform/lib/queries/org-summary.ts b/platform/lib/queries/org-summary.ts index 9cfc879..68631b6 100644 --- a/platform/lib/queries/org-summary.ts +++ b/platform/lib/queries/org-summary.ts @@ -1192,26 +1192,34 @@ export function computeHyperEngineers( } } - return [...authors.entries()] - .map(([key, a]) => { - // userMap is keyed by github-when-known, so this hits directly when - // `key` is already a github username. Otherwise, fall back to trying - // every name variant we saw for this person, in case userMap's own - // fallback (name-keyed) entry matches one of them. - const userInfo = - userMap.get(key) ?? - [...a.names].map((n) => userMap.get(n.toLowerCase())).find(Boolean); - const displayName = - userInfo?.name ?? [...a.names].sort((x, y) => y.length - x.length)[0]; - return { - name: displayName, - github: a.github ?? userInfo?.github, - repos: a.repos, - highVelocityWeeks: a.hvWeeks, - aiCommitPct: a.aiCount > 0 ? a.aiPct / a.aiCount : 0, - }; - }) - .sort((a, b) => b.repos - a.repos); + return ( + [...authors.entries()] + .map(([key, a]) => { + // userMap is keyed by github-when-known, so this hits directly when + // `key` is already a github username. Otherwise, fall back to trying + // every name variant we saw for this person, in case userMap's own + // fallback (name-keyed) entry matches one of them. + const userInfo = + userMap.get(key) ?? + [...a.names].map((n) => userMap.get(n.toLowerCase())).find(Boolean); + const displayName = + userInfo?.name ?? [...a.names].sort((x, y) => y.length - x.length)[0]; + return { + name: displayName, + github: a.github ?? userInfo?.github, + repos: a.repos, + highVelocityWeeks: a.hvWeeks, + aiCommitPct: a.aiCount > 0 ? a.aiPct / a.aiCount : 0, + }; + }) + // Without a resolved GitHub username there's no profile to link to, + // and — since that's also the strongest identity signal grouping has — + // no-github entries are the ones most likely to still be an + // unresolved duplicate of someone already shown with their GitHub + // entry. Better to omit them than show a name that may not be unique. + .filter((eng) => !!eng.github) + .sort((a, b) => b.repos - a.repos) + ); } // DORA aggregation moved to `lib/queries/dora.ts` — it reads directly diff --git a/platform/tests/org-summary.test.ts b/platform/tests/org-summary.test.ts index 23af816..bc3b2a3 100644 --- a/platform/tests/org-summary.test.ts +++ b/platform/tests/org-summary.test.ts @@ -391,7 +391,7 @@ describe("computeHyperEngineers — dedupes the same person across name variants expect(result[0].repos).toBe(2); }); - it("merges two display-name variants sharing a GitHub noreply email", () => { + it("merges two display-name variants sharing a GitHub noreply email, surfacing github from userMap once merged", () => { const payloads = new Map([ [ "repo-a", @@ -420,13 +420,22 @@ describe("computeHyperEngineers — dedupes the same person across name variants }), ], ]); + // Nothing in nameToGithub — the shared noreply email is what ties the + // two name variants into one group; userMap (keyed by the normalized + // email, which for a noreply address IS the github username) is what + // then supplies the github field for display. + const userMap = new Map([ + [ + "renatoguimaraescb", + { name: "Renato Guimarães", github: "renatoguimaraescb" }, + ], + ]); - // No github resolved anywhere — only the shared noreply email ties - // the two name variants together. - const result = computeHyperEngineers(payloads, new Map(), new Map()); + const result = computeHyperEngineers(payloads, userMap, new Map()); expect(result).toHaveLength(1); expect(result[0].repos).toBe(2); + expect(result[0].github).toBe("renatoguimaraescb"); }); it("keeps genuinely different people separate", () => { @@ -448,9 +457,35 @@ describe("computeHyperEngineers — dedupes the same person across name variants }), ], ]); + const nameToGithub = new Map([ + ["alice", "alice-gh"], + ["bob", "bob-gh"], + ]); - const result = computeHyperEngineers(payloads, new Map(), new Map()); + const result = computeHyperEngineers(payloads, new Map(), nameToGithub); expect(result).toHaveLength(2); }); + + it("omits an engineer with no resolved GitHub username — nothing to link to, and the least reliably deduped case", () => { + const payloads = new Map([ + [ + "repo-a", + payload({ + author_velocity: { + authors: [ + hyperAuthor({ + name: "Mystery Person", + email: "mystery@corp.com", + }), + ], + }, + }), + ], + ]); + + const result = computeHyperEngineers(payloads, new Map(), new Map()); + + expect(result).toHaveLength(0); + }); }); From cfc18486d4b58e0256f95545d7e871475c1e5a00 Mon Sep 17 00:00:00 2001 From: Rodrigo Matos Date: Thu, 27 Aug 2026 07:22:38 -0300 Subject: [PATCH 2/2] fix(platform): split Hyper Engineers into identified/unidentified instead of hiding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revises the previous commit on this branch: instead of filtering out engineers with no resolved GitHub username, computeHyperEngineers goes back to returning everyone (github left undefined when unresolved), and HyperEngineers.tsx splits the display into two labeled groups — "Identified" (has a linkable GitHub profile) and "Unidentified (no linked GitHub account)". Nobody's dropped from the view; the split just makes clear which entries are a confirmed identity versus one iris/cli.py couldn't tie to a GitHub account. The "Identified" header itself only renders when there's an unidentified group too, so orgs where everyone resolved cleanly don't get punished with an extra empty-feeling label. Co-Authored-By: claude-code_2-1-238_agent --- platform/lib/queries/org-summary.ts | 48 +++---- platform/lib/translations.ts | 4 + .../dashboard/sections/HyperEngineers.tsx | 120 ++++++++++++------ platform/tests/org-summary.test.ts | 7 +- 4 files changed, 110 insertions(+), 69 deletions(-) diff --git a/platform/lib/queries/org-summary.ts b/platform/lib/queries/org-summary.ts index 68631b6..9cfc879 100644 --- a/platform/lib/queries/org-summary.ts +++ b/platform/lib/queries/org-summary.ts @@ -1192,34 +1192,26 @@ export function computeHyperEngineers( } } - return ( - [...authors.entries()] - .map(([key, a]) => { - // userMap is keyed by github-when-known, so this hits directly when - // `key` is already a github username. Otherwise, fall back to trying - // every name variant we saw for this person, in case userMap's own - // fallback (name-keyed) entry matches one of them. - const userInfo = - userMap.get(key) ?? - [...a.names].map((n) => userMap.get(n.toLowerCase())).find(Boolean); - const displayName = - userInfo?.name ?? [...a.names].sort((x, y) => y.length - x.length)[0]; - return { - name: displayName, - github: a.github ?? userInfo?.github, - repos: a.repos, - highVelocityWeeks: a.hvWeeks, - aiCommitPct: a.aiCount > 0 ? a.aiPct / a.aiCount : 0, - }; - }) - // Without a resolved GitHub username there's no profile to link to, - // and — since that's also the strongest identity signal grouping has — - // no-github entries are the ones most likely to still be an - // unresolved duplicate of someone already shown with their GitHub - // entry. Better to omit them than show a name that may not be unique. - .filter((eng) => !!eng.github) - .sort((a, b) => b.repos - a.repos) - ); + return [...authors.entries()] + .map(([key, a]) => { + // userMap is keyed by github-when-known, so this hits directly when + // `key` is already a github username. Otherwise, fall back to trying + // every name variant we saw for this person, in case userMap's own + // fallback (name-keyed) entry matches one of them. + const userInfo = + userMap.get(key) ?? + [...a.names].map((n) => userMap.get(n.toLowerCase())).find(Boolean); + const displayName = + userInfo?.name ?? [...a.names].sort((x, y) => y.length - x.length)[0]; + return { + name: displayName, + github: a.github ?? userInfo?.github, + repos: a.repos, + highVelocityWeeks: a.hvWeeks, + aiCommitPct: a.aiCount > 0 ? a.aiPct / a.aiCount : 0, + }; + }) + .sort((a, b) => b.repos - a.repos); } // DORA aggregation moved to `lib/queries/dora.ts` — it reads directly diff --git a/platform/lib/translations.ts b/platform/lib/translations.ts index 7d8f1aa..940c10d 100644 --- a/platform/lib/translations.ts +++ b/platform/lib/translations.ts @@ -448,6 +448,8 @@ export const translations = { "Contributors with high velocity or 80%+ AI adoption across the org", badge: "Hyper Engineer", repos: "{count} repos", + identified: "Identified", + unidentified: "Unidentified (no linked GitHub account)", }, repoList: { empty: "No repositories yet.", @@ -1868,6 +1870,8 @@ export const translations = { "Contribuidores com alta velocidade ou 80%+ de adoção de IA na organização", badge: "Hyper Engineer", repos: "{count} repos", + identified: "Identificados", + unidentified: "Não identificados (sem conta do GitHub vinculada)", }, repoList: { empty: "Nenhum repositório ainda.", diff --git a/platform/src/app/[tenant]/dashboard/sections/HyperEngineers.tsx b/platform/src/app/[tenant]/dashboard/sections/HyperEngineers.tsx index a58661c..896cb4e 100644 --- a/platform/src/app/[tenant]/dashboard/sections/HyperEngineers.tsx +++ b/platform/src/app/[tenant]/dashboard/sections/HyperEngineers.tsx @@ -1,60 +1,102 @@ -'use client'; +"use client"; -import { GitHubAvatar } from '@/app/[tenant]/repos/[repoName]/github-avatar'; -import { useTranslation } from '@/hooks/useTranslation'; -import type { HyperEngineer } from '@/types/org-summary'; +import { GitHubAvatar } from "@/app/[tenant]/repos/[repoName]/github-avatar"; +import { useTranslation } from "@/hooks/useTranslation"; +import type { HyperEngineer } from "@/types/org-summary"; interface HyperEngineersProps { engineers: HyperEngineer[]; } +function EngineerCard({ + eng, + t, +}: { + eng: HyperEngineer; + t: ReturnType["t"]; +}) { + return ( +
+ {eng.github ? ( + + ) : ( +
+ {eng.name.charAt(0).toUpperCase()} +
+ )} + {eng.github ? ( + + {eng.name} + + ) : ( + {eng.name} + )} + 🏆 + {eng.repos > 1 && ( + + {t("dashboard.hyperEngineers.repos", { count: eng.repos })} + + )} +
+ ); +} + export function HyperEngineers({ engineers }: HyperEngineersProps) { const { t } = useTranslation(); if (engineers.length === 0) return null; + // Split by whether we could resolve a real GitHub identity. Two people + // (or two aliases of the same person iris/cli.py couldn't tie together — + // e.g. a personal email that isn't linked/verified on their GitHub + // account) can share a display name, so "identified" is the group we can + // actually confirm and link to a profile; "unidentified" is everyone else, + // shown separately rather than mixed in or hidden outright. + const identified = engineers.filter((eng) => eng.github); + const unidentified = engineers.filter((eng) => !eng.github); + return (
-

{t('dashboard.hyperEngineers.title')}

+

+ {t("dashboard.hyperEngineers.title")} +

- {t('dashboard.hyperEngineers.subtitle')} + {t("dashboard.hyperEngineers.subtitle")}

-
- {engineers.map((eng) => ( -
- {eng.github ? ( - - ) : ( -
- {eng.name.charAt(0).toUpperCase()} -
- )} - {eng.github ? ( - - {eng.name} - - ) : ( - {eng.name} - )} - 🏆 - {eng.repos > 1 && ( - - {t('dashboard.hyperEngineers.repos', { count: eng.repos })} - - )} + {identified.length > 0 && ( +
+ {unidentified.length > 0 && ( +

+ {t("dashboard.hyperEngineers.identified")} +

+ )} +
+ {identified.map((eng) => ( + + ))}
- ))} -
+
+ )} + + {unidentified.length > 0 && ( +
+

+ {t("dashboard.hyperEngineers.unidentified")} +

+
+ {unidentified.map((eng) => ( + + ))} +
+
+ )}
); } diff --git a/platform/tests/org-summary.test.ts b/platform/tests/org-summary.test.ts index bc3b2a3..495933b 100644 --- a/platform/tests/org-summary.test.ts +++ b/platform/tests/org-summary.test.ts @@ -467,7 +467,9 @@ describe("computeHyperEngineers — dedupes the same person across name variants expect(result).toHaveLength(2); }); - it("omits an engineer with no resolved GitHub username — nothing to link to, and the least reliably deduped case", () => { + it("still includes an engineer with no resolved GitHub username, with github left undefined", () => { + // computeHyperEngineers doesn't filter these out — the "identified" vs + // "unidentified" split is a display concern, done in HyperEngineers.tsx. const payloads = new Map([ [ "repo-a", @@ -486,6 +488,7 @@ describe("computeHyperEngineers — dedupes the same person across name variants const result = computeHyperEngineers(payloads, new Map(), new Map()); - expect(result).toHaveLength(0); + expect(result).toHaveLength(1); + expect(result[0].github).toBeUndefined(); }); });