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:
- 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.
- 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
- Have the Gittensor miner-detail API return a repository evaluation with a missing/non-string
repositoryFullName and nonzero totalPrs/totalOpenIssues/totalClosedIssues.
- Build the contributor snapshot/profile.
- 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).
Summary
buildGittensorContributorSnapshot(src/gittensor/api.ts:246) filters the repositories list by activity only, not by a validrepoFullName— unlike the sibling pull-request and issue lists right beside it, which both require a non-emptyrepoFullName:#437("guard Gittensor repo name normalization") maderepositoryFullNamean untrustedunknownand coerces malformed/missing values to""viaasString(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 withrepoFullName: "".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 intosnapshot.repositoriesas{ repoFullName: "", pullRequests: 5, ... }.Observable impact
snapshot.repositoriesfeedsbuildGittensorContributorProfile(src/signals/engine.ts):So an empty-name repo with nonzero stats:
evidenceScorebyreposTouched.length * 10(one phantom repo = +10 on a 0-100 scale), which feedstrustSignals.level(new< 25,emerging25-59,established>= 60). A malformed upstream row can bump a contributor's trust level a tier.""entry inregisteredRepoActivity.reposTouchedand in the returnedgittensor.repositorieslist (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
repositoryFullNameand nonzerototalPrs/totalOpenIssues/totalClosedIssues.snapshot.repositoriescontains an entry withrepoFullName: "",reposTouchedcontains"", andevidenceScoreis 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'sreposTouched, and theevidenceScore.Suggested fix
Add the same
repoFullNameguard the PR/issue lists already use: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 fromreposTouched/evidenceScore).