Skip to content

feat(miner-discovery): pure ranker function — potential x feasibility x laneFit x freshness x (1-dupRisk) #2302

Description

@JSONbored

Implement the core Phase 1 ranker: a pure function that composes five existing/adjacent deterministic signals into a single ordinal opportunity score for a cross-repo candidate-issue list, so gittensory_find_opportunities (a later issue) has something deterministic to sort by. No IO, no network, no randomness — the same inputs always produce the same ranked order, matching the house pattern already used in src/signals/duplicate-winner.ts ("This module is PURE — no IO, no Date, no random").

Deliverables

  • packages/gittensory-engine/lib/opportunity-ranker.tsexport type OpportunityRankInput = { potential: number; feasibility: number; laneFit: number; freshness: number; dupRisk: number } (each a 0-1 normalized score) and export function rankOpportunityScore(input: OpportunityRankInput): number computing potential * feasibility * laneFit * freshness * (1 - dupRisk), clamping every input field to [0, 1] before multiplying so a malformed upstream signal (e.g. dupRisk = 1.4) can't invert or blow up the product.
  • export function rankOpportunities<T>(candidates: Array<T & OpportunityRankInput>): Array<T & OpportunityRankInput & { rankScore: number }> — sorts descending by rankOpportunityScore, stable-sorted on tie (preserve input order for equal scores, mirroring the tie-break convention in isDuplicateClusterWinnerByClaim, src/signals/duplicate-winner.ts:43).
  • Unit tests: each of the five factors independently at 0 collapses the product to 0 (five separate cases); a dupRisk of exactly 1 zeroes the score; out-of-range inputs (negative, >1) are clamped, not passed through raw; the multi-candidate sort is stable on exact ties and correctly orders a 3+ candidate list.
  • A doc-comment on rankOpportunityScore explaining how feasibility should be sourced from src/services/issue-quality.ts's existing per-repo report, laneFit from MinerGoalSpec.preferredLanes (from the goal-model issue), and freshness/dupRisk from src/signals/reward-risk.ts's freshnessFactor / competitionFactor (~lines 88-93) and src/signals/duplicate-winner.ts's claim-adjudication respectively — so the composing caller (a later issue) has an explicit signal-source map.

References

  • src/signals/duplicate-winner.ts (60 lines) — the PURE-module convention ("no IO, no Date, no random") and the tie-break-by-claim-time pattern (isDuplicateClusterWinnerByClaim, line 43)
  • src/signals/reward-risk.ts lines 88-93 — existing competitionFactor / freshnessFactor opportunity factors this ranker composes
  • src/services/issue-quality.ts (52 lines) — existing per-repo feasibility/quality source
  • src/signals/local-scorer.ts (34 lines) — sibling deterministic-scorer style to match (input clamping, Math.max(0, ...) guards)
  • new path: packages/gittensory-engine/lib/opportunity-ranker.ts (this issue creates it)

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:featureGittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions