feat(miner-ui): add a ranked-candidates dashboard route - #7706
Conversation
/api/ranked-candidates already exposes the browser extension's opportunity-badge data -- the last discover run's full per-issue discovery breakdown (laneFit/freshness/potential/feasibility/dupRisk) -- but no miner-ui dashboard route consumed it. Adds a read-only Ranked candidates route mirroring run-history.tsx's exact data-fetching (usePolledFetch), loading-state (StateBoundary + a content-shaped Skeleton), and pagination conventions. No changes to the ranking logic or the existing /api/ranked-candidates endpoint. Closes JSONbored#7675
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-21 09:57:43 UTC
Review summary Nits — 4 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Visual previewClick any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. Scroll preview
A short scroll-through clip (desktop) — click either thumbnail to open the full animation. Evidence for scroll-linked behavior a single screenshot can't show. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|



Summary
/api/ranked-candidates(backed bypackages/loopover-miner/lib/ranked-candidates.js, wired intothe dev server by
apps/loopover-miner-ui/vite-ranked-candidates-api.ts) already exposes the lastdiscover run's full per-issue discovery breakdown (
laneFit/freshness/potential/feasibility/dupRisk) for the browser extension's opportunity badge, but noapps/loopover-miner-uidashboardroute consumed it. This adds a read-only Ranked candidates route that renders it.
Mirrored route:
apps/loopover-miner-ui/src/routes/run-history.tsx(named per the issue'srequirement). The new route copies its exact conventions:
usePolledFetch+DEFAULT_POLL_INTERVAL_MSfor the fetch/poll loop.StateBoundaryfor the loading/error/empty states, with a content-shapedSkeletontable so thelayout doesn't jump once the poll resolves.
Paginationonce the table exceeds 20 rows, unpaginated below it.src/lib/ranked-candidates.ts(mirroringsrc/lib/run-history.ts) holding the typedfetch client, payload-shape validation, and demo-mode branch — the route file itself stays
presentation-only.
DEMO_RANKED_CANDIDATESfixture added tosrc/lib/demo-data.tsalongside the existing demofixtures, so the route also works in the app's zero-backend demo build.
__root.tsx's nav, next to "Run history".This is a strictly read-only view of already-existing data:
vite-ranked-candidates-api.tsandpackages/loopover-miner/lib/ranked-candidates.js's ranking logic are both untouched.Scope
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm --workspace apps/loopover-miner-ui run typecheck(rootnpm run typecheckOOMs on thissandbox; ran the scoped equivalent instead, after
npm run ui:kit:build+npm run build:minerso
@loopover/ui-kitand@loopover/engineresolve)npm --workspace apps/loopover-miner-ui run lintnpx vitest runinapps/loopover-miner-ui(30 files, 350 tests, all passing, including the30 new/updated tests for this route)
npm run test:coverage— not run;apps/**is outside Codecov'scoverage.include(confirmedvia
codecov.yml), so this UI-only change carries no patch-coverage obligation. Test coverageis instead validated per the issue's own Test Coverage Requirements: real behavior tests
matching
run-history.test.tsx's conventions (seesrc/ranked-candidates.test.tsx).npm run test:workers/npm run build:mcp/npm run test:mcp-pack/npm run ui:openapi:check— not applicable; no backend, workers, MCP, or OpenAPI-surface changes.
npm --workspace apps/loopover-miner-ui run build(regeneratedrouteTree.gen.tsfor the new/ranked-candidatesroute, committed)npm audit --audit-level=moderateSafety
/api/ranked-candidatesitself is untouched (already had its own test coverage inranked-candidates-api.test.ts).DEMO_RANKED_CANDIDATESfixture only activates under the existing build-timeVITE_DEMO_MODEflag, same as every other route's demo data).UI Evidencesection with screenshots — attempted but not included; see the note below.If any required check was skipped, explain why:
VITE_DEMO_MODE=1) hit a pre-existing sandboxenvironment crash — the client bundle pulls in
packages/loopover-engine/dist/miner/deny-hook-synthesis.js,which calls
node:crypto'screateHashat module load time, and Vite's browser externalizationthrows before React can mount. This reproduces identically on the already-shipped, unmodified
/run-historyroute in this same sandbox, so it is not caused by this change. Validation insteadrests on the full green typecheck/lint/build/test suite above, and the new route's structure is a
line-for-line mirror of
run-history.tsx's already-shipped, already-screenshotted layout.Notes
apps/loopover-miner-ui/src/lib/ranked-candidates.ts(typed fetch client + payloadvalidation, mirrors
lib/run-history.ts),apps/loopover-miner-ui/src/routes/ranked-candidates.tsx(the route, mirrors
routes/run-history.tsx),apps/loopover-miner-ui/src/ranked-candidates.test.tsx(route + lib tests, mirrors
run-history.test.tsx's cases: fixture rendering, loading skeleton,error/empty states, pagination above/below 20 rows, live-refresh polling, fetch payload validation
including both sides of the
htmlUrl: string | nullbranch, and demo-mode).src/lib/demo-data.ts(+DEMO_RANKED_CANDIDATESfixture, scope comment updated),src/lib/demo-data.test.ts(+ a shape-invariant test for the new fixture),src/routes/__root.tsx(+nav entry),
src/routeTree.gen.ts(regenerated byvite buildfor the new route, per the repo'sgenerated-artifact convention).