feat(miner): persist ranked-candidates snapshots and serve them locally - #5619
Conversation
Adds packages/gittensory-miner/lib/ranked-candidates.js: a new snapshot store that discover-cli.js now populates on every real run with the full per-issue ranking breakdown (rankScore/laneFit/ freshness/potential/feasibility/dupRisk), replaced wholesale each run. Nothing durable held this before -- discover --json printed it but never persisted it. Adds apps/gittensory-miner-ui/vite-ranked-candidates-api.ts: a read-only GET /api/ranked-candidates endpoint over that store, authenticated the same way as every other /api/* route via #4858's authPlugin. This is the prerequisite for #4859 (extension live-fetch) -- the extension's opportunity badge needs exactly this per-issue breakdown to replace its manual copy/paste workflow, and no data source existed for it until now.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | 9e15246 | Commit Preview URL Branch Preview URL |
Jul 13 2026, 10:38 AM |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5619 +/- ##
=======================================
Coverage 95.01% 95.01%
=======================================
Files 574 575 +1
Lines 45684 45738 +54
Branches 14661 14661
=======================================
+ Hits 43405 43459 +54
Misses 1528 1528
Partials 751 751
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-13 10:43:37 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 6 non-blocking
Concerns raised — review before merging
📋 Copy for AI agents — paste into your coding agent
Review context
Contributor next steps
Signal definitions
[BETA] Chat with GittensoryAsk Gittensory 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://gittensory.aethereal.dev/docs/gittensory-commands Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. 🟩 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.
|
…ner UI (#5622) Adds syncRankedCandidatesFromMinerUi() to background.js, pulling from #5619's GET /api/ranked-candidates and writing into the same chrome.storage.local keys the manual-paste flow already writes -- so content.js/opportunity-badge.js/toolbar-badge.js need zero changes. Triggers on startup/install and every 10 minutes via chrome.alarms, plus a manual "Sync now" button. A failed sync leaves storage untouched, naturally falling back to whatever's already there (a stale fetch or a manual paste) with no merge logic needed. Adds a "Local miner UI URL" field to the options page (default http://localhost:5174, persisted to chrome.storage.sync alongside watchedRepos).


Summary
packages/gittensory-miner/lib/ranked-candidates.js: a new local SQLite snapshot store.discover-cli.js'srunDiscovernow persists the FULL per-issue ranking breakdown (rankScore/laneFit/freshness/potential/feasibility/dupRisk, fromopportunity-ranker.js) on every real (non-dry-run) invocation, replacing the whole snapshot atomically each time — a disposable "the miner's current opinion" cache, not a ledger.apps/gittensory-miner-ui/vite-ranked-candidates-api.ts: a read-onlyGET /api/ranked-candidatesendpoint over that store, authenticated the same way as every other/api/*route via Add auth to the local miner-ui API #4858'sauthPlugin(registered first in the plugin chain).discover-cli.js: same "own try/catch, degrade gracefully" discipline as the existing policy-doc/policy-verdict caches — an unopenable or unwritable snapshot store never aborts discovery's actual job (fan out, rank, enqueue to the portfolio queue).Why
#4859 ("Replace the manual copy/paste workflow with a live fetch") wants the browser extension to fetch ranked candidates from the local miner-ui instead of the operator hand-pasting
discover --json's output. But nothing durable held that output anywhere —discover --jsonprints the full breakdown to stdout and it's gone; the portfolio queue only ever stored a single derivedprioritynumber, not the per-dimension detailopportunity-badge.js's "why" reasoning needs (laneFit/freshness/potential/feasibility/dupRisk). This PR builds that missing persistence + read API as the prerequisite; the extension's own fetch/fallback logic is a separate follow-up PR.Advances #4859 (not closing it — the extension-side consumer is still open).
Test plan
npm run build:miner,npm run typechecknpm run miner:env-reference:check(newGITTENSORY_MINER_RANKED_CANDIDATES_DBvar documented)npm run docs:drift-check,npm run test:miner-packnpm run ui:typecheck,npm run ui:lint(0 errors),npm run ui:buildtest/unit/miner-ranked-candidates.test.ts, 11 cases): round-trip, atomic wholesale replace (a second save wipes the first, never accumulates), neutral-default dimension fallback, invalid-candidate rejection (aborts the whole save, no partial write), and a genuine SQL-level rollback test (a duplicate repo+issue within one save forces a real PRIMARY KEY constraint failure, proving the transaction wrapper actually rolls back)discover-cli.jsintegration tests (4 new cases intest/unit/miner-discover-cli.test.ts): persists the real snapshot after a run, opens/closes the default on-disk store, and two REGRESSION tests proving an unopenable store or a save failure never fails discovery itselfranked-candidates-api.test.ts, 8 cases): handler + fresh-install-safe GET path + the actual Vite middleware wiring (configureServer/configurePreviewServer)packages/gittensory-miner/lib/ranked-candidates.jsanddiscover-cli.js: 100% line/branch/function coverage confirmed via lcov on my diff's changed lines specifically (one pre-existing branch gap indiscover-cli.js'sparseRepoTarget, outside this diff's hunks, is unaffected)GET /api/ranked-candidatesauthenticated (200, correct shape matching whatopportunity-badge.jsneeds) and unauthenticated (401)Note: while writing the transactional replace, hit a real bug —
node:sqlite'sDatabaseSynchas no.transaction()helper (unlikebetter-sqlite3); fixed by mirroringportfolio-queue.js's explicitBEGIN IMMEDIATE/COMMIT/ROLLBACKpattern. Also hit and worked around a v8 coverage-instrumentation quirk where anif (x) { try {} catch {} }shape undercounted a genuinely-exercised branch (verified via direct runtime instrumentation) — restructured tox?.method()inside the try/catch, which both reads cleaner and reports correctly.