Skip to content

ams: opportunity-ranker silently drops assignees despite RankedCandidateIssue's type promising it #9330

Description

@JSONbored

⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.

Context

RankedCandidateIssue = RawCandidateIssue & {...score fields} in
packages/loopover-miner/lib/opportunity-ranker.ts (~line 9), and RawCandidateIssue
(opportunity-fanout.ts, ~lines 39-54) includes assignees: string[], populated by
normalizeIssue (#7040).

normalizeCandidate in opportunity-ranker.ts (~lines 42-76) explicitly re-copies every other
field from the input candidate onto its return object — owner, repo, repoFullName,
issueNumber, title, labels, commentsCount, createdAt, updatedAt, htmlUrl,
aiPolicyAllowed, aiPolicySource — but never copies assignees.

Today's exclusion logic isn't broken by this: discover-cli.ts runs filterCandidatesByProfiles
(the repo-owner exclusion filter) on raw fan-out candidates before ranking, so it still sees the
real assignees field on the pre-ranked candidates. But rankIssues(kept, ...)'s output becomes
result.ranked in discover --json's output and options.onResult's payload — and that ranked,
post-normalizeCandidate output silently omits assignees despite RankedCandidateIssue's own
type promising the field is there (via the RawCandidateIssue & intersection).

Requirements

  • normalizeCandidate must copy assignees from the input candidate onto its return object,
    following the same normalization discipline as the existing labels field (an array field
    already handled with a type-safe filter/map): validate it's an array of strings, defaulting to
    [] if the field is missing or malformed — mirror labels's exact validation shape
    (Array.isArray(candidate.assignees) ? candidate.assignees.filter(...).map(...) : [], adjusted
    for assignees not needing a .trim() if the existing RawCandidateIssue/normalizeIssue
    producer never puts untrimmed values there — check normalizeIssue in the relevant fan-out file
    to confirm whether trimming is needed for consistency).
  • Do not change any other field's normalization in this function.
  • Do not change RawCandidateIssue, RankedCandidateIssue, or normalizeIssue — only
    normalizeCandidate's copying of the already-typed assignees field.

Deliverables

  • normalizeCandidate in opportunity-ranker.ts copies assignees onto its return object,
    validated the same way labels already is.
  • A new test asserting a candidate with a populated assignees array survives
    normalizeCandidate/rankIssues with assignees present and correct in the ranked output.
  • A new test asserting a candidate with a missing or malformed assignees field normalizes to
    [] rather than throwing or propagating an invalid value.

All three deliverables are required in this single PR.

Test Coverage Requirements

This repo enforces 99%+ Codecov patch coverage, branch-counted, on all changed lines/branches
(applies to packages/loopover-miner/lib/**). The new copying logic and both new test cases must
be covered.

Expected Outcome

discover --json's ranked output (and options.onResult's payload) now actually includes
assignees for every ranked candidate, matching what RankedCandidateIssue's type already
promises, instead of silently dropping a field consumers may reasonably expect to be present.

Links & Resources

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions