Skip to content

[Bug]: Gittensor snapshot keeps empty-name repos, inflating evidenceScore #444

Description

@galuis116

Summary

buildGittensorContributorSnapshot (src/gittensor/api.ts:246) filters the repositories list by activity only, not by a valid repoFullName — unlike the sibling pull-request and issue lists right beside it, which both require a non-empty repoFullName:

// src/gittensor/api.ts:246-249
repositories: (detail.repositories ?? []).map(toRepositoryEvaluation).filter((repo) => repo.pullRequests + repo.openIssues + repo.closedIssues > 0), // <- no repoFullName guard
pullRequests: pullRequests.map(toPullRequest).filter((pr) => pr.repoFullName && pr.number > 0),    // guarded
// ...
issues: issues.map(toIssue).filter((issue) => issue.repoFullName && issue.number > 0),             // guarded

#437 ("guard Gittensor repo name normalization") made repositoryFullName an untrusted unknown and coerces malformed/missing values to "" via asString (toRepositoryEvaluation, line ~267). But it did not add the empty-name filter to the repositories list, so a malformed repo evaluation (non-string/missing name) with nonzero stats now passes through with repoFullName: "".

Why this is wrong

The PR and issue lists exclude entries with an empty repoFullName, so the maintainer's intent (after #437 made names untrusted) is clearly to drop entries whose name failed to normalize. The repositories list is the lone outlier, so a malformed repo with real activity counts leaks into snapshot.repositories as { repoFullName: "", pullRequests: 5, ... }.

Observable impact

snapshot.repositories feeds buildGittensorContributorProfile (src/signals/engine.ts):

// engine.ts:1119
const reposTouched = snapshot.repositories
  .filter((repo) => repo.pullRequests + repo.openIssues + repo.closedIssues > 0)   // empty-name repo passes
  .map((repo) => repo.repoFullName)                                                // -> ""
  .sort();
// engine.ts:1137
const evidenceScore = clamp(/* ... */ + reposTouched.length * 10 + /* ... */, 0, 100);

So an empty-name repo with nonzero stats:

  1. Inflates evidenceScore by reposTouched.length * 10 (one phantom repo = +10 on a 0-100 scale), which feeds trustSignals.level (new < 25, emerging 25-59, established >= 60). A malformed upstream row can bump a contributor's trust level a tier.
  2. Appears as a phantom "" entry in registeredRepoActivity.reposTouched and in the returned gittensor.repositories list (engine.ts:1169).

This is reachable in production because #437 itself treats upstream Gittensor repository names as untrusted JSON that can be malformed.

Steps to reproduce

  1. Have the Gittensor miner-detail API return a repository evaluation with a missing/non-string repositoryFullName and nonzero totalPrs/totalOpenIssues/totalClosedIssues.
  2. Build the contributor snapshot/profile.
  3. Observe snapshot.repositories contains an entry with repoFullName: "", reposTouched contains "", and evidenceScore is 10 points higher than it should be.

Expected behavior

The repositories list excludes entries with an empty repoFullName, consistent with the pull-request and issue lists and with #437's intent.

Actual behavior

The repositories list keeps any entry with nonzero activity regardless of repoFullName, so malformed/empty-name repos leak into the snapshot, the contributor's reposTouched, and the evidenceScore.

Suggested fix

Add the same repoFullName guard the PR/issue lists already use:

repositories: (detail.repositories ?? [])
  .map(toRepositoryEvaluation)
  .filter((repo) => repo.repoFullName && repo.pullRequests + repo.openIssues + repo.closedIssues > 0),

Add fail-on-revert coverage: a snapshot built from a repository evaluation with an empty/non-string name and nonzero stats must exclude it from snapshot.repositories (and therefore from reposTouched/evidenceScore).

Metadata

Metadata

Assignees

No one assigned

    Labels

    slopAI slop and/or attempts to game additional points via manipulation or alt profiles.

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions