Description
On the Miner Details page's Pull Requests table (/miners/details?githubId=<id>&tab=pull-requests), the Score column's sort is broken for open PRs and the Repository column is not sortable at all.
- Score sort sorts by a different field than the cell displays. The cell renders
collateralScore for open unmerged PRs, score for merged PRs, and - for closed-unmerged. The sort comparator unconditionally reads pr.score, so every open PR is treated as score = 0 and clicking the Score header does not reorder the open-PR rows.
- Repository column has no sort control. The header is a plain
<TableCell>Repository</TableCell> with no <TableSortLabel>, so users cannot group the table by repo.
Steps to Reproduce
- Open
https://gittensor.io/miners/details?githubId=42954461&tab=pull-requests (any miner with a mix of merged and open PRs reproduces it — this one has 17 open PRs with non-zero collateral, making the bug visually obvious).
- Click the Score column header one or more times. Observe the open-PR rows (the ones with a "Collateral" sublabel under the value).
- Look at the Repository column header and try to click it.
Expected Behavior
- Clicking Score should reorder the rows by the value the cell actually displays — i.e., by
collateralScore for open PRs and score for merged PRs. Highest first on the first click, lowest first after a second click.
- The Repository column header should be a sortable control like the other columns. Clicking it should group rows alphabetically by
owner/repo (ascending on first click, descending on second), with a deterministic tiebreaker within a single repo.
Actual Behavior
- Clicking Score leaves the open-PR rows in whatever order the API returned. Verified against miner
42954461 via the test API: all 17 open PRs have pr.score = "0.000000" while their collateralScore ranges from 0.001 to 0.370. The comparator sees 17 zeros and cannot order them, so the displayed collateral column appears to ignore the sort click.
- The Repository column header is static text. There is no sort indicator, no hover affordance, and clicking it does nothing. The only way to group by repo today is a substring search, which misses casing differences and picks up partial matches.
Environment
- Browser: reproduced on latest Chrome and Firefox (not browser-specific — the bug is in the sort comparator, not the rendering)
- OS: any
- URL:
https://gittensor.io/miners/details?githubId=42954461&tab=pull-requests
- Branch: reproduced on both
main and test as of 2026-04-15
Additional Context
Videos
Before: https://github.com/user-attachments/assets/49bceb35-5d15-4e45-be55-096c3185fa98
Clicking the Score header on miner 42954461 — the open-PR rows (with the "Collateral" sublabel) stay in the same order across clicks because the comparator reads pr.score = 0 for all of them. The Repository header is not clickable.
After: https://github.com/user-attachments/assets/b26870e1-8835-4233-bdbe-61b91bf6b6bf
Same miner, same clicks. Score now reorders open-PR rows by their displayed collateral value (highest-first on the first click, lowest-first after toggling). Repository is a sortable header that groups rows by owner/repo.
Root cause
src/components/miners/MinerPRsTable.tsx in the sortedPRs useMemo:
case 'score':
cmp = parseFloat(a.score || '0') - parseFloat(b.score || '0');
break;
Meanwhile the Score cell's render chain further down in the same file reads:
pr.prState === 'CLOSED' && !pr.mergedAt
? '-'
: !pr.mergedAt && pr.collateralScore
? parseFloat(pr.collateralScore).toFixed(4) // "Collateral" label
: parseFloat(pr.score).toFixed(4)
The sort comparator and the renderer read different fields. For merged PRs they agree. For open PRs with collateral they diverge, and all open PRs collapse to the same sort key.
The Repository header:
<TableCell sx={{ ...headerCellStyle, width: '25%' }}>
Repository
</TableCell>
No <TableSortLabel>, no click handler, no 'repository' entry in the PrSortField type.
Proposed fix
- Extract a
getEffectiveScore(pr) helper that mirrors the render fallback exactly — CLOSED && !mergedAt → 0, !mergedAt && collateralScore → parseFloat(collateralScore), else parseFloat(score). Use it in the 'score' case so the sort key matches the displayed value.
- Add
'repository' to PrSortField and implement the comparator with a.repository.localeCompare(b.repository), falling back to a.pullRequestNumber - b.pullRequestNumber as a deterministic tiebreaker within a single repo.
- Introduce a
DEFAULT_SORT_DIR: Record<PrSortField, SortDir> map so each column has a self-documenting natural direction — string columns ascending, numeric/date columns descending — and handleSort reads from it instead of branching on the field name. Keeps future sortable columns (Title, Author, etc.) a one-line addition.
- Wrap the Repository header in
<TableSortLabel>, consistent with the PR #, +/-, Score, and Date headers. Idle-state direction comes from DEFAULT_SORT_DIR.repository.
Scope
- Frontend only, no API or data-shape changes.
- Single file:
src/components/miners/MinerPRsTable.tsx (~40 lines added, ~4 removed).
- No visual regression for currently-working columns (PR #, +/-, Date).
Description
On the Miner Details page's Pull Requests table (
/miners/details?githubId=<id>&tab=pull-requests), the Score column's sort is broken for open PRs and the Repository column is not sortable at all.collateralScorefor open unmerged PRs,scorefor merged PRs, and-for closed-unmerged. The sort comparator unconditionally readspr.score, so every open PR is treated asscore = 0and clicking the Score header does not reorder the open-PR rows.<TableCell>Repository</TableCell>with no<TableSortLabel>, so users cannot group the table by repo.Steps to Reproduce
https://gittensor.io/miners/details?githubId=42954461&tab=pull-requests(any miner with a mix of merged and open PRs reproduces it — this one has 17 open PRs with non-zero collateral, making the bug visually obvious).Expected Behavior
collateralScorefor open PRs andscorefor merged PRs. Highest first on the first click, lowest first after a second click.owner/repo(ascending on first click, descending on second), with a deterministic tiebreaker within a single repo.Actual Behavior
42954461via the test API: all 17 open PRs havepr.score = "0.000000"while theircollateralScoreranges from0.001to0.370. The comparator sees 17 zeros and cannot order them, so the displayed collateral column appears to ignore the sort click.Environment
https://gittensor.io/miners/details?githubId=42954461&tab=pull-requestsmainandtestas of 2026-04-15Additional Context
Videos
Before: https://github.com/user-attachments/assets/49bceb35-5d15-4e45-be55-096c3185fa98
Clicking the Score header on miner
42954461— the open-PR rows (with the "Collateral" sublabel) stay in the same order across clicks because the comparator readspr.score = 0for all of them. The Repository header is not clickable.After: https://github.com/user-attachments/assets/b26870e1-8835-4233-bdbe-61b91bf6b6bf
Same miner, same clicks. Score now reorders open-PR rows by their displayed collateral value (highest-first on the first click, lowest-first after toggling). Repository is a sortable header that groups rows by
owner/repo.Root cause
src/components/miners/MinerPRsTable.tsxin thesortedPRsuseMemo:Meanwhile the Score cell's render chain further down in the same file reads:
The sort comparator and the renderer read different fields. For merged PRs they agree. For open PRs with collateral they diverge, and all open PRs collapse to the same sort key.
The Repository header:
No
<TableSortLabel>, no click handler, no'repository'entry in thePrSortFieldtype.Proposed fix
getEffectiveScore(pr)helper that mirrors the render fallback exactly —CLOSED && !mergedAt → 0,!mergedAt && collateralScore → parseFloat(collateralScore), elseparseFloat(score). Use it in the'score'case so the sort key matches the displayed value.'repository'toPrSortFieldand implement the comparator witha.repository.localeCompare(b.repository), falling back toa.pullRequestNumber - b.pullRequestNumberas a deterministic tiebreaker within a single repo.DEFAULT_SORT_DIR: Record<PrSortField, SortDir>map so each column has a self-documenting natural direction — string columns ascending, numeric/date columns descending — andhandleSortreads from it instead of branching on the field name. Keeps future sortable columns (Title, Author, etc.) a one-line addition.<TableSortLabel>, consistent with the PR #, +/-, Score, and Date headers. Idle-state direction comes fromDEFAULT_SORT_DIR.repository.Scope
src/components/miners/MinerPRsTable.tsx(~40 lines added, ~4 removed).