Summary
Adds a new Fairness Signals card to the bottom of the Repo Check tab on every repository details page (RepositoryCheckTab). The card flags miners whose pull requests merge noticeably faster than the rest of the repo — a leading indicator of maintainer favoritism, sockpuppet accounts, or self-dealing — before a miner commits time to contributing.
The card has three coordinated panels:
- Fastest median time-to-merge (TTM) — a ranked top-5 leaderboard of miners with the shortest median TTM in this repo. Each row shows the miner, their median TTM, merged-PR count, and a percent delta vs the repo median (e.g. "91% faster than repo median").
- vs Repo baseline chart — a horizontal ECharts bar chart of the same top 5, with a dashed reference line at the repo median TTM. When the repo median is more than ~1.5× the slowest top-5 bar (skewed distributions like
entrius/gittensor), the line is suppressed and the value is surfaced as a caption below the chart ("Repo Median: 6d 12h") so the bars stay readable.
- Detail table — one row per qualifying miner with Miner · Merged PRs · Median TTM · Reject Rate. Miners whose median TTM is below the repo median are highlighted red across the whole row (name, bar, value label, and y-axis label in the chart), making the "this miner is being fast-tracked" pattern visible at a glance.
Miner names link to /miners/details?githubId={id}, matching the existing pattern from RepositoryMinersTab.
Motivation
A repo's emission share is finite. If maintainers are consistently fast-tracking PRs from a small set of favored accounts while letting other miners' PRs sit for days, new miners are effectively competing for whatever scoring slack remains — without any way to know that before they start. Today the Repo Check tab tells contributors whether the repo has a CONTRIBUTING.md, but says nothing about whether the repo's PR pipeline is fair.
Surfacing this pattern lets miners:
- Spot repos with an obvious favored author before investing time in PRs that will languish
- Compare merge speeds across repos when deciding where to allocate effort
- Self-audit — a maintainer running the tool on their own repo sees the same picture validators see, and can address bias before it costs them credibility
The framing in the card is intentionally signals to investigate, not verdicts — colored cells are outliers vs the repo's own distribution, not proof of self-dealing. This matches the existing language around collateralScore, credibilityMultiplier, and the TrustBadge component.
Scope
| Surface |
Change |
RepositoryCheckTab |
New FairnessSignalsCard rendered at the bottom of the tab |
| Data sources |
useRepositoryMiners (canonical PR counts), useAllPrs (TTM timestamps), useRepositoryMaintainers (filter) |
| Counted miners |
totalMergedPrs ≥ 1, maintainers excluded by login (case-insensitive) |
| Median TTM |
median(mergedAt − prCreatedAt) per author, hours |
| Repo baseline |
Median of all non-maintainer miners' TTMs in this repo |
| Highlight rule |
Row + chart turn red when miner's median TTM < repo median |
| Eligibility |
Green dot next to login, tooltip shows credibility |
| Empty states |
"No miner PR activity recorded" / "No merged PRs yet — no TTM to rank" |
| Miner name link |
/miners/details?githubId={githubId} (consistent with RepositoryMinersTab) |
Out of scope: top-merger %, self-merge count, takeover/duplicate-PR detection (each requires per-PR detail fetches and per-PR GitHub /pulls/{n}/files calls; deferred to a follow-up issue once the v1 design is validated). No change to existing Health Score, Activity, Issue Analysis, or Community Standards sections of RepositoryCheckTab.
Technical notes
- New component:
src/components/repositories/FairnessSignalsCard.tsx, wired into src/components/repositories/RepositoryCheckTab.tsx via a single <FairnessSignalsCard repositoryFullName={repositoryFullName} /> at the end of the Grid.
- Case-insensitive repo match for
pr.repository vs repositoryFullName — backend stores mkdev11/gittensor-hub while frontend routes use canonical MkDev11/gittensor-hub. Exact-match comparison silently drops every PR; both sides lowercased before comparison.
- Authoritative counts come from
m.totalMergedPrs / m.totalClosedPrs on the RepositoryMiner row, not inferred from filtering /prs. The /prs endpoint excludes some historical closed PRs that the backend has already counted, so deriving counts from it diverges from the Miners tab — e.g. Khaostica on touchpilot/touchpilot (3 merged + 1 closed in the miner row, but 3 merged + 1 open in /prs).
- TTM is computed from
/prs because that's where the per-PR mergedAt / prCreatedAt timestamps live; the miner row only carries aggregates.
- Maintainer filter: build a
Set<string> of useRepositoryMaintainers(repo) logins, lowercased; skip any miner whose githubUsername.toLowerCase() is in the set. Maintainers' PRs also don't feed the repo-baseline median.
- Median (not mean) for TTM: even-count lists take the average of the two middle values. Mean was tried briefly but discarded because a single legitimate slow PR drags the value up disproportionately.
- Chart rendering uses the existing
echartsAxisTooltipChrome / echartsFontFamily / echartsMutedCartesianAxisColors / echartsTransparentBackground helpers from src/utils/echarts/gittensorChartTheme.ts. Per-bar and per-axis-label coloring uses item-level itemStyle.color / label.color and rich-text {r|name} / {n|name} tokens in axisLabel.rich — ECharts v6 ignores callback functions on those fields.
- Off-chart median fallback: when
repoMedianTtm > maxBarValue * 1.5, clamp xAxis.max to maxBarValue * 1.15, set markLine.data to [], and render a <Typography> caption below the chart reading Repo Median: {formatTtm(repoMedianTtm)}.
- Style parity: card chrome (border, radius, padding, header icon + subtitle pattern) matches the existing Health Score / Activity & Feasibility / Issue Analysis cards in
RepositoryCheckTab.
Summary
Adds a new Fairness Signals card to the bottom of the Repo Check tab on every repository details page (
RepositoryCheckTab). The card flags miners whose pull requests merge noticeably faster than the rest of the repo — a leading indicator of maintainer favoritism, sockpuppet accounts, or self-dealing — before a miner commits time to contributing.The card has three coordinated panels:
entrius/gittensor), the line is suppressed and the value is surfaced as a caption below the chart ("Repo Median: 6d 12h") so the bars stay readable.Miner names link to
/miners/details?githubId={id}, matching the existing pattern fromRepositoryMinersTab.Motivation
A repo's emission share is finite. If maintainers are consistently fast-tracking PRs from a small set of favored accounts while letting other miners' PRs sit for days, new miners are effectively competing for whatever scoring slack remains — without any way to know that before they start. Today the Repo Check tab tells contributors whether the repo has a CONTRIBUTING.md, but says nothing about whether the repo's PR pipeline is fair.
Surfacing this pattern lets miners:
The framing in the card is intentionally signals to investigate, not verdicts — colored cells are outliers vs the repo's own distribution, not proof of self-dealing. This matches the existing language around
collateralScore,credibilityMultiplier, and theTrustBadgecomponent.Scope
RepositoryCheckTabFairnessSignalsCardrendered at the bottom of the tabuseRepositoryMiners(canonical PR counts),useAllPrs(TTM timestamps),useRepositoryMaintainers(filter)totalMergedPrs ≥ 1, maintainers excluded by login (case-insensitive)median(mergedAt − prCreatedAt)per author, hourscredibility/miners/details?githubId={githubId}(consistent withRepositoryMinersTab)Out of scope: top-merger %, self-merge count, takeover/duplicate-PR detection (each requires per-PR detail fetches and per-PR GitHub
/pulls/{n}/filescalls; deferred to a follow-up issue once the v1 design is validated). No change to existing Health Score, Activity, Issue Analysis, or Community Standards sections ofRepositoryCheckTab.Technical notes
src/components/repositories/FairnessSignalsCard.tsx, wired intosrc/components/repositories/RepositoryCheckTab.tsxvia a single<FairnessSignalsCard repositoryFullName={repositoryFullName} />at the end of the Grid.pr.repositoryvsrepositoryFullName— backend storesmkdev11/gittensor-hubwhile frontend routes use canonicalMkDev11/gittensor-hub. Exact-match comparison silently drops every PR; both sides lowercased before comparison.m.totalMergedPrs/m.totalClosedPrson theRepositoryMinerrow, not inferred from filtering/prs. The/prsendpoint excludes some historical closed PRs that the backend has already counted, so deriving counts from it diverges from the Miners tab — e.g. Khaostica ontouchpilot/touchpilot(3 merged + 1 closed in the miner row, but 3 merged + 1 open in/prs)./prsbecause that's where the per-PRmergedAt/prCreatedAttimestamps live; the miner row only carries aggregates.Set<string>ofuseRepositoryMaintainers(repo)logins, lowercased; skip any miner whosegithubUsername.toLowerCase()is in the set. Maintainers' PRs also don't feed the repo-baseline median.echartsAxisTooltipChrome/echartsFontFamily/echartsMutedCartesianAxisColors/echartsTransparentBackgroundhelpers fromsrc/utils/echarts/gittensorChartTheme.ts. Per-bar and per-axis-label coloring uses item-levelitemStyle.color/label.colorand rich-text{r|name}/{n|name}tokens inaxisLabel.rich— ECharts v6 ignores callback functions on those fields.repoMedianTtm > maxBarValue * 1.5, clampxAxis.maxtomaxBarValue * 1.15, setmarkLine.datato[], and render a<Typography>caption below the chart readingRepo Median: {formatTtm(repoMedianTtm)}.RepositoryCheckTab.