Skip to content

fix(scoring): guard credibility against non-finite stale and unlinked counts - #1155

Merged
JSONbored merged 5 commits into
JSONbored:mainfrom
dripsmvcp:fix/infer-credibility-finite-guard
Jun 24, 2026
Merged

fix(scoring): guard credibility against non-finite stale and unlinked counts#1155
JSONbored merged 5 commits into
JSONbored:mainfrom
dripsmvcp:fix/infer-credibility-finite-guard

Conversation

@dripsmvcp

Copy link
Copy Markdown
Contributor

Summary

inferCredibility (in src/scoring/preview.ts) derives a contributor credibility multiplier from cached evidence counts. It coerces mergedPullRequests, stalePullRequests, and unlinkedPullRequests with Number(...), but only finiteness-guards merged:

if (!Number.isFinite(merged)) return 0.8;            // guards merged ONLY
return clamp(0.75 + merged * 0.04 - stale * 0.03 - unlinked * 0.02, 0.25, 1);

The evidence payload is a loosely-typed cache (Record<string, JsonValue>), so any of these fields can arrive as a non-numeric cached value (string, object, …). A non-finite stale or unlinked propagates NaN into the arithmetic, and clamp cannot rescue it (Math.min(1, Math.max(0.25, NaN)) is NaN) — so the whole credibility multiplier, and through buildScorePreview the gates.credibilityObserved and scoreEstimate.estimatedMergedScore, become NaN. The identical garbage in mergedPullRequests is already tolerated (it returns the neutral 0.8); only stale/unlinked were left unguarded.

Fix: extend the existing guard to all three counts, so a malformed stale/unlinked degrades to the same neutral 0.8 fallback that a malformed merged already produces, keeping the score finite. Pure helper; no signature or public-surface change.

No linked issue: this is a small, self-evident defensive-correctness fix to one pure scoring helper, fully described above; per CONTRIBUTING.md / .gittensory.yml (linkedIssuePolicy: preferred) a focused fix like this does not require a linked issue.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked an issue, or this is small enough that the summary explains why an issue is not needed (no-issue rationale above).

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally — both newly guarded branches (stale and unlinked non-finite) and the well-formed arithmetic path are covered; the changed lines are at 100% line and branch coverage and global coverage stays at/above the 97% line/branch threshold.
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New behavior has unit tests for the new branches and the fallback path (test/unit/scoring.test.ts).

If any required check was skipped, explain why:

  • npm run test:ci (which wraps git diff --check, actionlint, db:migrations:check, typecheck, test:coverage, test:workers, build:mcp, test:mcp-pack, ui:openapi:check, ui:version-audit, ui:lint, ui:typecheck, ui:test, ui:build) ran fully green locally.
  • npm audit --audit-level=moderate surfaces only one pre-existing transitive advisory (undici) that this PR does not introduce — the diff changes no dependency manifest or lockfile, so the dependency-review job is unaffected (that ambient advisory is tracked by the scheduled audit.yml workflow + Renovate, not by PR CI).

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed (numeric-only pure-function change).
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics (no public-comment surface touched).
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests — N/A: no auth/cookie/CORS/GitHub App/Cloudflare/session changes.
  • API/OpenAPI/MCP behavior is updated and tested where needed — N/A: no API/OpenAPI/MCP surface change (internal helper only; ui:openapi:check passes unchanged).
  • UI changes use live API data or real empty/error/loading states — N/A: no UI changes.
  • Public docs/changelogs are updated where needed — N/A: no docs/changelog changes.

Notes

  • The added regression test asserts each malformed-count case yields credibilityObserved === 0.8 and a finite estimatedMergedScore, and that well-formed counts still flow through the arithmetic (so the guard does not over-fire).

… counts

`inferCredibility` coerces the cached evidence counts `mergedPullRequests`,
`stalePullRequests`, and `unlinkedPullRequests` with `Number(...)`, but only
finiteness-guards `merged`. Because the evidence payload is a loosely-typed
cache (`Record<string, JsonValue>`), any count can arrive non-numeric; a
non-finite `stale` or `unlinked` propagates `NaN` through the multiplier
arithmetic, and `clamp(Math.min(1, Math.max(0.25, NaN)))` cannot rescue it, so
the whole credibility multiplier and the resulting estimated score become `NaN`.

Extend the existing guard to all three counts so a malformed `stale`/`unlinked`
degrades to the same neutral 0.8 fallback that a malformed `merged` already
produced, keeping the score finite. Adds a regression test covering each newly
guarded branch plus the well-formed arithmetic path.
@dripsmvcp
dripsmvcp requested a review from JSONbored as a code owner June 24, 2026 03:32
@dosubot dosubot Bot added the size:XS label Jun 24, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.00%. Comparing base (7b50835) to head (e2e880e).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1155   +/-   ##
=======================================
  Coverage   95.00%   95.00%           
=======================================
  Files         177      177           
  Lines       19827    19827           
  Branches     7133     7133           
=======================================
  Hits        18837    18837           
  Misses        396      396           
  Partials      594      594           
Files with missing lines Coverage Δ
src/scoring/preview.ts 98.82% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ghost

ghost commented Jun 24, 2026

Copy link
Copy Markdown

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review — safe to merge

2 files · 2 AI reviewers · no blockers · readiness 100/100 · CI green

✅ Approved — safe to merge

Review summary
The change expands the finiteness guard in `inferCredibility` to include stale and unlinked pull request counts, preventing NaN propagation and ensuring a fallback credibility of 0.8. A new test validates this behavior for malformed stale and unlinked values. All existing tests pass, confirming functional correctness.

Nits (5)

  • The added comment in `preview.ts` is quite long and could be split for readability.
  • The guard logic could be extracted into a small helper to avoid repetition and improve testability.
  • The test imports `JsonValue` solely for type annotation; the import could be omitted at runtime.
  • Consider refactoring the finiteness check into a utility function like `isValidCount` and reuse it in `inferCredibility`.
  • Add a test case where all three counts are non-finite to confirm the guard short-circuits early.
Signal Result Evidence
Code review ✅ No blockers 2 reviewers, synthesized
Linked issue ✅ No-issue rationale PR body explains why no issue is linked.
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Review load ✅ 20/20 Readiness component derived from cached public PR metadata and labels; size label size:XS.
Validation evidence ✅ 25/25 PR body includes validation/test evidence.
Open PR queue ✅ 10/10 0 open PR(s), 0 likely reviewable.
Contributor context ✅ Confirmed Gittensor contributor dripsmvcp; Gittensor profile; 1070 PR(s), 3 issue(s).
Gate result ✅ Passing No configured blocker found.
Nits — 1 non-blocking
  • No linked issue detected — If this PR is intended to solve an issue, link it explicitly in the PR body.
Review context
  • Author: dripsmvcp
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Rust, MDX, Python, TypeScript
  • Official Gittensor activity: 1070 PR(s), 3 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Link the issue being solved, or explicitly explain why this is a no-issue PR.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Review load = cached public PR metadata such as size labels, changed paths, and preflight status.
  • Open PR queue = repo-wide review pressure; it is not a PR quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
Review details

Generated from public PR metadata and the diff. Advisory only; deterministic signals remain authoritative.

The change expands the finiteness guard in `inferCredibility` to include stale and unlinked pull request counts, preventing NaN propagation and ensuring a fallback credibility of 0.8. A new test validates this behavior for malformed stale and unlinked values. All existing tests pass, confirming functional correctness.

Nits (5)

  • The added comment in `preview.ts` is quite long and could be split for readability.
  • The guard logic could be extracted into a small helper to avoid repetition and improve testability.
  • The test imports `JsonValue` solely for type annotation; the import could be omitted at runtime.
  • Consider refactoring the finiteness check into a utility function like `isValidCount` and reuse it in `inferCredibility`.
  • Add a test case where all three counts are non-finite to confirm the guard short-circuits early.

🟩 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 Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@ghost ghost added gittensory:reviewed gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jun 24, 2026

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MERGE. fix(scoring): guard credibility against non-finite stale/unlinked counts.

  • Real bug: inferCredibility only finite-guarded merged; a non-numeric cached
    stale/unlinked count coerces to NaN and clamp(NaN,…)=NaN poisons credibilityObserved,
    credibilityMultiplier, estimatedMergedScore (preview.ts:331-333, 365-366).
  • Fix correct & well-placed: guard broadened to all three counts before the arithmetic;
    Number.isFinite catches NaN+Infinity; fallback 0.8 == MIN_CREDIBILITY so the multiplier
    becomes 1 — no div-by-zero / downstream regression.
  • Coverage complete & meaningful: every || arm tested both ways; asserts
    credibilityObserved===0.8 per malformed branch + toBeCloseTo(0.87,5) on the happy path.
  • Already current with main; scope clean; no attribution.
    Note (non-blocking): commits are authored as "plind-junior" while the PR is @dripsmvcp.

@dosubot dosubot Bot added the lgtm label Jun 24, 2026
@JSONbored
JSONbored merged commit e6a7597 into JSONbored:main Jun 24, 2026
16 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 25, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants